Skip to content

Commit 6e94006

Browse files
committed
Initial additions
1 parent ff00dbe commit 6e94006

File tree

6 files changed

+369
-0
lines changed

6 files changed

+369
-0
lines changed

Diff for: .gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "src/client"]
2+
path = src/client
3+
url = https://github.com/mariadb-developers/places-app-client.git

Diff for: README.md

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# Places
2+
3+
**Places** is a web application backed by the power of the power, performance, and simplicity of [MariaDB](https://mariadb.com/), allows you to record all of your favorite locations using both structured (relational) and semi-structured (JSON) data!
4+
5+
<p align="center" spacing="10">
6+
<kbd>
7+
<img src="media/map.png" />
8+
</kbd>
9+
</p>
10+
11+
This application is made of two parts:
12+
13+
* Client
14+
- web UI that communicates with REST endpoints available through an API app (see below).
15+
- is a React.js project located in the [client](src/client) folder.
16+
* API
17+
- uses the [MariaDB Python Connector](https://github.com/mariadb-corporation/mariadb-connector-python) to connect to MariaDB.
18+
- is a Python project located int the [api](src/api) folder.
19+
20+
This README will walk you through the steps for getting the `Places` web application up and running using MariaDB.
21+
22+
# Table of Contents
23+
1. [Requirements](#requirements)
24+
2. [Getting started with MariaDB and JSON](#mariadb)
25+
2. [Get the code](#code)
26+
4. [Configure, build and run the apps](#app)
27+
1. [Configure](#configure-api-app)
28+
2. [Create and activate a Python virtual environment](#activate-virtual-env)
29+
3. [Install Python packages](#install-python-packages)
30+
4. [Build and run the Python API app](#build-run-api)
31+
5. [Build and run the Client app](#build-run-client)
32+
5. [JSON Data Models](#data-models)
33+
6. [Support and contribution](#support-contribution)
34+
7. [License](#license)
35+
36+
## Requirements <a name="requirements"></a>
37+
38+
This sample application requires the following to be installed/enabled on your machine:
39+
40+
* [Python (v. 3+)](https://www.python.org/downloads/)
41+
* [MariaDB Connector/C (v. 3.1.5+)](https://mariadb.com/products/skysql/docs/clients/mariadb-connector-c-for-skysql-services/) (used by Connector/Python)
42+
* [Node.js (v. 12+)](https://nodejs.org/docs/latest-v12.x/api/index.html) (for the Client/UI app)
43+
* [NPM (v. 6+)](https://docs.npmjs.com/) (for the Client/UI app)
44+
* [MariaDB command-line client](https://mariadb.com/products/skysql/docs/clients/mariadb-clients/mariadb-client/) (optional), used to connect to MariaDB database instances.
45+
46+
## 1.) Getting Started with MariaDB and JSON <a name="mariadb"></a>
47+
48+
Set up a MariaDB database, loaded with the data this sample needs, using the [MariaDB JSON Quickstart](https://github.com/mariadb-developers/mariadb-json-quickstart), before continuing to the next step.
49+
50+
## 2.) Get the code <a name="code"></a>
51+
52+
First, use [git](git-scm.org) (through CLI or a client) to retrieve the code using `git clone`:
53+
Ts
54+
```
55+
$ git clone https://github.com/mariadb-developers/places-app-python.git
56+
```
57+
58+
Next, because this repo uses a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules), you will need to pull the [client application](https://github.com/mariadb-developers/todo-app-client) using:
59+
60+
```bash
61+
$ git submodule update --init --recursive
62+
```
63+
64+
## 3.) Configure, Build and Run the App <a name="app"></a>
65+
66+
This application is made of two parts:
67+
68+
* Client
69+
- web UI that communicates with REST endpoints available through an API app (see below).
70+
- is a React.js project located in the [client](src/client) folder.
71+
* API
72+
- uses the [MariaDB Python Connector](https://github.com/mariadb-corporation/mariadb-connector-python) to connect to MariaDB.
73+
- is a Python project located int the [api](src/api) folder.
74+
75+
### a.) Configure the app <a name="configure-app"></a>
76+
77+
Configure the MariaDB connection by [adding an .env file to the Node.js project](https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/promise-api.md#security-consideration). Add the .env file [here](src/api) (the `api` folder).
78+
79+
Example implementation:
80+
81+
```
82+
DB_HOST=<host_address>
83+
DB_PORT=<port_number>
84+
DB_USER=<username>
85+
DB_PASS=<password>
86+
DB_NAME=places
87+
```
88+
89+
**Configuring db.js**
90+
91+
The environmental variables from `.env` are used within [src/api/tasks.py](src/api/tasks.py) for the MariaDB Python Connector connection configuration settings:
92+
93+
```python
94+
config = {
95+
'host': os.getenv("DB_HOST"),
96+
'port': int(os.getenv("DB_PORT")),
97+
'user': os.getenv("DB_USER"),
98+
'password': os.getenv("DB_PASS"),
99+
'database': os.getenv("DB_NAME")
100+
}
101+
```
102+
103+
Note: MariaDB SkySQL requires SSL additions to connection. Details coming soon!
104+
105+
### b.) Create and activate a Python virtual environment <a name="activate-virtual-env"></a>
106+
107+
A virtual environment is a directory tree which contains Python executable files and other files which indicate that it is a virtual environment. Basically, it's the backbone for running your Python Flask app.
108+
109+
Creation of [virtual environments](https://docs.python.org/3/library/venv.html?ref=hackernoon.com#venv-def) is done by executing the following command (within [/src/api](src/api)):
110+
111+
```
112+
$ python3 -m venv venv
113+
```
114+
115+
**Tip**: Tip: pyvenv is only available in Python 3.4 or later. For older versions please use the [virtualenv](https://virtualenv.pypa.io/en/latest/) tool.
116+
117+
Before you can start installing or using packages in your virtual environment, you’ll need to activate it. Activating a virtual environment will put the virtual environment-specific python and pip executables into your shell’s PATH.
118+
119+
Activate the virtual environment using the following command (within [/src/api](src/api)):
120+
121+
```bash
122+
$ . venv/bin/activate activate
123+
```
124+
125+
### c.) Install Python packages <a name="install-python-packages"></a>
126+
127+
[Flask](https://flask.palletsprojects.com/en/1.1.x/?ref=hackernoon.com) is a micro web framework written in Python. It is classified as a [microframework](https://en.wikipedia.org/wiki/Microframework) because it does not require particular tools or libraries.
128+
129+
**TL;DR** It's what this app uses for the API.
130+
131+
This app also uses the MariaDB Python Connector to connect to and communicate with MariaDB databases.
132+
133+
Install the necessary packages by executing the following command (within [/src/api](src/api)):
134+
135+
```bash
136+
$ pip3 install flask mariadb python-dotenv
137+
```
138+
139+
### d.) Build and run the [API app](src/api) <a name="build-run-api"></a>
140+
141+
Once you've pulled down the code and have verified that all of the required packages are installed you're ready to run the application!
142+
143+
From [/src/api](src/api) execute the following CLI command to start the the Python project, which will start the application on https://localhost:8080.
144+
145+
```bash
146+
$ python3 api.py
147+
```
148+
149+
### c.) Build and run the [UI (Client) app](src/client) <a name="build-run-client"></a>
150+
151+
Once the API project is running you can now communicate with the exposed endpoints directly (via HTTP requests) or with the application UI, which is contained with the [client](src/client) folder of this repo.
152+
153+
To start the [client](src/client) application follow the instructions [here](https://github.com/mariadb-developers/places-app-client).
154+
155+
## JSON Data Models <a name="data-models"></a>
156+
157+
Below are samples of the data model per Location Type.
158+
159+
**Attraction (A)**
160+
```json
161+
{
162+
"category":"Landmark",
163+
"lastVisitDate":"11/5/2019"
164+
}
165+
```
166+
167+
**Restuarant (R)**
168+
```json
169+
{
170+
"details":{
171+
"foodType":"Pizza",
172+
"menu":"www.giodanos.com/menu"
173+
},
174+
"favorites":[
175+
{
176+
"description":"Classic Chicago",
177+
"price":24.99
178+
},
179+
{
180+
"description":"Salad",
181+
"price":9.99
182+
}
183+
]
184+
}
185+
```
186+
187+
**Sports Venue (S)**
188+
```json
189+
{
190+
"details":{
191+
"yearOpened":1994,
192+
"capacity":23500
193+
},
194+
"events":[
195+
{
196+
"date":"10/18/2019",
197+
"description":"Bulls vs Celtics"
198+
},
199+
{
200+
"date":"10/21/2019",
201+
"description":"Bulls vs Lakers"
202+
},
203+
{
204+
"date":"11/5/2019",
205+
"description":"Bulls vs Bucks"
206+
},
207+
{
208+
"date":"11/5/2019",
209+
"description":"Blackhawks vs Blues"
210+
}
211+
]
212+
}
213+
```
214+
215+
## Support and Contribution <a name="support-contribution"></a>
216+
217+
Please feel free to submit PR's, issues or requests to this project project directly.
218+
219+
If you have any other questions, comments, or looking for more information on MariaDB please check out:
220+
221+
* [MariaDB Developer Hub](https://mariadb.com/developers)
222+
* [MariaDB Community Slack](https://r.mariadb.com/join-community-slack)
223+
224+
Or reach out to us diretly via:
225+
226+
227+
* [MariaDB Twitter](https://twitter.com/mariadb)
228+
229+
## License <a name="license"></a>
230+
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=plastic)](https://opensource.org/licenses/MIT)

Diff for: media/map.png

443 KB
Loading

Diff for: src/api/api.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import flask
2+
from locations import locations
3+
4+
app = flask.Flask(__name__)
5+
app.config["DEBUG"] = True
6+
app.register_blueprint(locations)
7+
8+
@app.route("/api/version")
9+
def version():
10+
return "1.0"
11+
12+
app.run(port=8080)

Diff for: src/api/locations.py

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import sys
2+
import simplejson as json
3+
import mariadb
4+
import os
5+
import flask
6+
from flask import request
7+
from flask import Blueprint
8+
from dotenv import load_dotenv
9+
10+
load_dotenv()
11+
12+
locations = Blueprint('locations', __name__)
13+
14+
config = {
15+
'host': os.getenv("DB_HOST"),
16+
'port': int(os.getenv("DB_PORT")),
17+
'user': os.getenv("DB_USER"),
18+
'password': os.getenv("DB_PASS"),
19+
'database': os.getenv("DB_NAME")
20+
}
21+
22+
@locations.route('/api/locations', methods=['GET','POST','PUT','DELETE'])
23+
def index():
24+
conn = mariadb.connect(**config)
25+
cur = conn.cursor()
26+
json_data = []
27+
28+
if request.method == 'GET':
29+
query = "select id, name, type, longitude, latitude, " \
30+
"case when type = 'R' then concat((case when json_length(attr, '$.favorites') " \
31+
"is not null then json_length(attr, '$.favorites') else 0 end), ' favorite meals') " \
32+
"when type = 'A' then (case when json_value(attr, '$.lastVisitDate') is not null " \
33+
"then json_value(attr, '$.lastVisitDate') else 'N/A' end) " \
34+
"when type = 'S' then concat((case when json_length(attr, '$.events') is not null " \
35+
"then json_length(attr, '$.events') else 0 end), ' events') end as description " \
36+
"from locations"
37+
cur.execute(query)
38+
row_headers=[x[0] for x in cur.description]
39+
rv = cur.fetchall()
40+
for result in rv:
41+
json_data.append(dict(zip(row_headers,result)))
42+
43+
if request.method == 'POST':
44+
name = request.json['name']
45+
description = request.json['description']
46+
type = request.json['type']
47+
latitude = request.json['latitude']
48+
longitude = request.json['longitude']
49+
attr = request.json['attr']
50+
query = "insert into locations (name, description, type, latitude, longitude, attr) values (?, ?, ?, ?, ?, json_compact(?))"
51+
cur.execute(query,(name, description, type, latitude, longitude, attr))
52+
json_data = { 'success': True }
53+
54+
return json.dumps(json_data), 200, {'ContentType':'application/json'}
55+
56+
@locations.route('/api/locations/restaurant', methods=['GET'])
57+
def get_restaurant():
58+
id = request.args.get('id')
59+
conn = mariadb.connect(**config)
60+
cur = conn.cursor()
61+
json_data = []
62+
query = "select " \
63+
"name, " \
64+
"json_value(attr,'$.details.foodType') as foodType, " \
65+
"json_value(attr,'$.details.menu') as menu, " \
66+
"json_query(attr,'$.favorites') as favorites " \
67+
"from locations " \
68+
"where id = ?"
69+
cur.execute(query, [id])
70+
row_headers=[x[0] for x in cur.description]
71+
result = cur.fetchone()
72+
json_obj=dict(zip(row_headers,result))
73+
return json.dumps(json_obj), 200, {'ContentType':'application/json'}
74+
75+
@locations.route('/api/locations/restaurant/favorites', methods=['POST'])
76+
def add_restaurant_favorite():
77+
id = request.json['locationId']
78+
details = request.json['details']
79+
conn = mariadb.connect(**config)
80+
cur = conn.cursor()
81+
query = "update locations set attr = json_array_append(attr, '$.favorites', json_compact(?)) where id = ?"
82+
cur.execute(query,[details,id])
83+
return json.dumps({ 'success': True }), 200, {'ContentType':'application/json'}
84+
85+
@locations.route('/api/locations/sportsvenue', methods=['GET'])
86+
def get_sportsvenue():
87+
id = request.args.get('id')
88+
conn = mariadb.connect(**config)
89+
cur = conn.cursor()
90+
json_data = []
91+
query = "select " \
92+
"name, " \
93+
"json_value(attr,'$.details.yearOpened') as yearOpened, " \
94+
"json_value(attr,'$.details.capacity') as capacity, " \
95+
"json_query(attr,'$.events') as events " \
96+
"from locations " \
97+
"where id = ?"
98+
cur.execute(query, [id])
99+
print(query)
100+
row_headers=[x[0] for x in cur.description]
101+
result = cur.fetchone()
102+
json_obj=dict(zip(row_headers,result))
103+
return json.dumps(json_obj), 200, {'ContentType':'application/json'}
104+
105+
@locations.route('/api/locations/sportsvenue/events', methods=['POST'])
106+
def add_sportsvenue_event():
107+
id = request.json['locationId']
108+
details = request.json['details']
109+
conn = mariadb.connect(**config)
110+
cur = conn.cursor()
111+
query = "update locations set attr = json_array_append(attr, '$.events', json_compact(?)) where id = ?"
112+
cur.execute(query,[details,id])
113+
return json.dumps({ 'success': True }), 200, {'ContentType':'application/json'}
114+
115+
@locations.route('/api/locations/attractions', methods=['PUT'])
116+
def attractions():
117+
locationId = request.args.get('id')
118+
lastVisitDate = request.args.get('dt')
119+
conn = mariadb.connect(**config)
120+
cur = conn.cursor()
121+
query = "update locations set attr = json_set(attr,'$.lastVisitDate', ?) where id = ?"
122+
cur.execute(query,[lastVisitDate,locationId])
123+
return json.dumps({ 'success': True }), 200, {'ContentType':'application/json'}

Diff for: src/client

Submodule client added at 68cb67c

0 commit comments

Comments
 (0)