Compare commits

..

No commits in common. "students" and "master" have entirely different histories.

8 changed files with 192 additions and 127 deletions

View File

@ -3,7 +3,7 @@
import os import os
from . import db from . import db
from .students import Students, Student from .contacts import Contacts, Contact
from flask import Flask, request, jsonify from flask import Flask, request, jsonify
from flask_restful import Api from flask_restful import Api
@ -15,8 +15,8 @@ def create_app(test_config=None):
db.init_app(app) db.init_app(app)
api = Api(app) api = Api(app)
CORS(app, resources={r"/api/*": {"origins": "*"}}) CORS(app, resources={r"/api/*": {"origins": "*"}})
api.add_resource(Students, '/api/students') api.add_resource(Contacts, '/api/contacts')
api.add_resource(Student, '/api/students/<int:uid>') api.add_resource(Contact, '/api/contacts/<int:uid>')
return app return app

View File

@ -8,28 +8,31 @@ from flask_restful import Resource, Api, reqparse
from .db import get_db from .db import get_db
class Students(Resource): class Contacts(Resource):
def get(self): def get(self):
db = get_db() db = get_db()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT uid, last_name_val, first_name_val, middle_name_val, discipline_val, mark" cur.execute("SELECT uid, last_name_val, first_name_val, middle_name_val, street_val, build, build_k, apartment, telephone"
" FROM main" " FROM main"
" join last_name on main.last_name = last_name_id" " join last_name on main.last_name = last_name_id"
" join first_name on main.first_name = first_name_id" " join first_name on main.first_name = first_name_id"
" join middle_name on main.middle_name = middle_name_id" " join middle_name on main.middle_name = middle_name_id"
" join discipline on main.discipline = discipline_id") " join street on main.street = street_id")
students = cur.fetchall() contacts = cur.fetchall()
def convert(data): def convert(data):
return { return {
'uid': data[0], 'uid': data[0],
'last_name': data[1], 'last_name': data[1],
'first_name': data[2], 'first_name': data[2],
'middle_name': data[3], 'middle_name': data[3],
'discipline': data[4], 'street': data[4],
'mark': data[5] 'build': data[5],
'build_k': data[6],
'apartment': data[7],
'telephone': data[8]
} }
students = [convert(student) for student in students] contacts = [convert(contact) for contact in contacts]
return students return contacts
def post(self): def post(self):
db = get_db() db = get_db()
@ -38,10 +41,13 @@ class Students(Resource):
parser.add_argument('last_name') parser.add_argument('last_name')
parser.add_argument('first_name') parser.add_argument('first_name')
parser.add_argument('middle_name') parser.add_argument('middle_name')
parser.add_argument('discipline') parser.add_argument('street')
parser.add_argument('mark') parser.add_argument('build')
parser.add_argument('build_k')
parser.add_argument('apartment')
parser.add_argument('telephone', required=True)
args = parser.parse_args() args = parser.parse_args()
cur.execute("SELECT uid FROM main ORDER BY uid DESC LIMIT 1") cur.execute("SELECT uid FROM main ORDER BY uid DESC LIMIT 1")
uid = cur.fetchone() uid = cur.fetchone()
if uid is None: if uid is None:
@ -78,77 +84,83 @@ class Students(Resource):
middle_name = middle_name[0] middle_name = middle_name[0]
else: else:
middle_name = middle_name[0] middle_name = middle_name[0]
cur.execute("SELECT discipline_id FROM discipline WHERE discipline_val = %s", (args['discipline'],)) cur.execute("SELECT street_id FROM street WHERE street_val = %s", (args['street'],))
discipline = cur.fetchone() street = cur.fetchone()
if discipline is None: if street is None:
cur.execute("INSERT INTO discipline (discipline_val) VALUES (%s)", (args['discipline'],)) cur.execute("INSERT INTO street (street_val) VALUES (%s)", (args['street'],))
cur.execute("SELECT discipline_id FROM discipline WHERE discipline_val = %s", (args['discipline'],)) cur.execute("SELECT street_id FROM street WHERE street_val = %s", (args['street'],))
discipline = cur.fetchone() street = cur.fetchone()
discipline = discipline[0] street = street[0]
else: else:
discipline = discipline[0] street = street[0]
cur.execute("INSERT INTO main (uid, last_name, first_name, middle_name, discipline, mark)"
" VALUES (%s, %s, %s, %s, %s, %s)",
(uid, last_name, first_name, middle_name, discipline, args['mark']))
cur.execute("INSERT INTO main (uid, last_name, first_name, middle_name, street, build, build_k, apartment, telephone)"
" VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
(uid, last_name, first_name, middle_name, street, args['build'], args['build_k'], args['apartment'], args['telephone']))
db.commit() db.commit()
return {'uid': uid}, 201 return {'uid': uid}, 201
class Student(Resource): class Contact(Resource):
def get(self, uid): def get(self, uid):
db = get_db() db = get_db()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT uid, last_name_val, first_name_val, middle_name_val, discipline_val, mark" cur.execute("SELECT uid, last_name_val, first_name_val, middle_name_val, street_val, build, build_k, apartment, telephone"
" FROM main" " FROM main"
" join last_name on main.last_name = last_name_id" " join last_name on main.last_name = last_name_id"
" join first_name on main.first_name = first_name_id" " join first_name on main.first_name = first_name_id"
" join middle_name on main.middle_name = middle_name_id" " join middle_name on main.middle_name = middle_name_id"
" join discipline on main.discipline = discipline_id" " join street on main.street = street_id"
" WHERE uid = %s", (uid,)) " WHERE uid = %s", (uid,))
student = cur.fetchone() contact = cur.fetchone()
if student is None: if contact is None:
return {'message': 'Student not found'}, 404 return {'message': 'Contact not found'}, 404
def convert(data): def convert(data):
return { return {
'uid': data[0], 'uid': data[0],
'last_name': data[1], 'last_name': data[1],
'first_name': data[2], 'first_name': data[2],
'middle_name': data[3], 'middle_name': data[3],
'discipline': data[4], 'street': data[4],
'mark': data[5] 'build': data[5],
'build_k': data[6],
'apartment': data[7],
'telephone': data[8]
} }
student = convert(student) contact = convert(contact)
return student return contact
def delete(self, uid): def delete(self, uid):
db = get_db() db = get_db()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT uid FROM main WHERE uid = %s", (uid,)) cur.execute("SELECT uid FROM main WHERE uid = %s", (uid,))
student = cur.fetchone() contact = cur.fetchone()
if student is None: if contact is None:
return {'message': 'Student not found'}, 404 return {'message': 'Contact not found'}, 404
cur.execute("DELETE FROM main WHERE uid = %s", (uid,)) cur.execute("DELETE FROM main WHERE uid = %s", (uid,))
db.commit() db.commit()
return {'message': 'Student deleted'} return {'message': 'Contact deleted'}
def put(self, uid): def put(self, uid):
parser = reqparse.RequestParser() parser = reqparse.RequestParser()
parser.add_argument('last_name') parser.add_argument('last_name')
parser.add_argument('first_name') parser.add_argument('first_name')
parser.add_argument('middle_name') parser.add_argument('middle_name')
parser.add_argument('discipline') parser.add_argument('street')
parser.add_argument('mark') parser.add_argument('build')
parser.add_argument('build_k')
parser.add_argument('apartment')
parser.add_argument('telephone')
args = parser.parse_args() args = parser.parse_args()
db = get_db() db = get_db()
cur = db.cursor() cur = db.cursor()
cur.execute("SELECT uid FROM main WHERE uid = %s", (uid,)) cur.execute("SELECT uid FROM main WHERE uid = %s", (uid,))
student = cur.fetchone() contact = cur.fetchone()
if student is None: if contact is None:
return {'message': 'Student not found'}, 404 return {'message': 'Contact not found'}, 404
if args['last_name'] is not None: if args['last_name'] is not None:
cur.execute("SELECT last_name_id FROM last_name WHERE last_name_val = %s", (args['last_name'],)) cur.execute("SELECT last_name_id FROM last_name WHERE last_name_val = %s", (args['last_name'],))
last_name = cur.fetchone() last_name = cur.fetchone()
@ -160,7 +172,7 @@ class Student(Resource):
else: else:
last_name = last_name[0] last_name = last_name[0]
cur.execute("UPDATE main SET last_name = %s WHERE uid = %s", (last_name, uid)) cur.execute("UPDATE main SET last_name = %s WHERE uid = %s", (last_name, uid))
if args['first_name'] is not None: if args['first_name'] is not None:
cur.execute("SELECT first_name_id FROM first_name WHERE first_name_val = %s", (args['first_name'],)) cur.execute("SELECT first_name_id FROM first_name WHERE first_name_val = %s", (args['first_name'],))
first_name = cur.fetchone() first_name = cur.fetchone()
@ -172,7 +184,7 @@ class Student(Resource):
else: else:
first_name = first_name[0] first_name = first_name[0]
cur.execute("UPDATE main SET first_name = %s WHERE uid = %s", (first_name, uid)) cur.execute("UPDATE main SET first_name = %s WHERE uid = %s", (first_name, uid))
if args['middle_name'] is not None: if args['middle_name'] is not None:
cur.execute("SELECT middle_name_id FROM middle_name WHERE middle_name_val = %s", (args['middle_name'],)) cur.execute("SELECT middle_name_id FROM middle_name WHERE middle_name_val = %s", (args['middle_name'],))
middle_name = cur.fetchone() middle_name = cur.fetchone()
@ -184,21 +196,30 @@ class Student(Resource):
else: else:
middle_name = middle_name[0] middle_name = middle_name[0]
cur.execute("UPDATE main SET middle_name = %s WHERE uid = %s", (middle_name, uid)) cur.execute("UPDATE main SET middle_name = %s WHERE uid = %s", (middle_name, uid))
if args['discipline'] is not None: if args['street'] is not None:
cur.execute("SELECT discipline_id FROM discipline WHERE discipline_val = %s", (args['discipline'],)) cur.execute("SELECT street_id FROM street WHERE street_val = %s", (args['street'],))
discipline = cur.fetchone() street = cur.fetchone()
if discipline is None: if street is None:
cur.execute("INSERT INTO discipline (discipline_val) VALUES (%s)", (args['discipline'],)) cur.execute("INSERT INTO street (street_val) VALUES (%s)", (args['street'],))
cur.execute("SELECT discipline_id FROM discipline WHERE discipline_val = %s", (args['discipline'],)) cur.execute("SELECT street_id FROM street WHERE street_val = %s", (args['street'],))
discipline = cur.fetchone() street = cur.fetchone()
discipline = discipline[0] street = street[0]
else: else:
discipline = discipline[0] street = street[0]
cur.execute("UPDATE main SET discipline = %s WHERE uid = %s", (discipline, uid)) cur.execute("UPDATE main SET street = %s WHERE uid = %s", (street, uid))
if args['mark'] is not None: if args['build'] is not None:
cur.execute("UPDATE main SET mark = %s WHERE uid = %s", (args['mark'], uid)) cur.execute("UPDATE main SET build = %s WHERE uid = %s", (args['build'], uid))
if args['build_k'] is not None:
cur.execute("UPDATE main SET build_k = %s WHERE uid = %s", (args['build_k'], uid))
if args['apartment'] is not None:
cur.execute("UPDATE main SET apartment = %s WHERE uid = %s", (args['apartment'], uid))
if args['telephone'] is not None:
cur.execute("UPDATE main SET telephone = %s WHERE uid = %s", (args['telephone'], uid))
db.commit() db.commit()
return {'message': 'Student updated'} return {'message': 'Contact updated'}

View File

@ -2,7 +2,7 @@ DROP TABLE IF EXISTS main;
DROP TABLE IF EXISTS first_name; DROP TABLE IF EXISTS first_name;
DROP TABLE IF EXISTS last_name; DROP TABLE IF EXISTS last_name;
DROP TABLE IF EXISTS middle_name; DROP TABLE IF EXISTS middle_name;
DROP TABLE IF EXISTS discipline; DROP TABLE IF EXISTS street;
CREATE TABLE first_name ( CREATE TABLE first_name (
first_name_id SERIAL PRIMARY KEY, first_name_id SERIAL PRIMARY KEY,
first_name_val VARCHAR(255) NOT NULL first_name_val VARCHAR(255) NOT NULL
@ -15,20 +15,23 @@ CREATE TABLE middle_name (
middle_name_id SERIAL PRIMARY KEY, middle_name_id SERIAL PRIMARY KEY,
middle_name_val VARCHAR(255) NOT NULL middle_name_val VARCHAR(255) NOT NULL
); );
CREATE TABLE discipline ( CREATE TABLE street (
discipline_id SERIAL PRIMARY KEY, street_id SERIAL PRIMARY KEY,
discipline_val VARCHAR(255) NOT NULL street_val VARCHAR(255) NOT NULL
); );
CREATE TABLE main ( CREATE TABLE main (
uid serial, uid serial,
last_name int, last_name int,
first_name int, first_name int,
middle_name int, middle_name int,
discipline int, street int,
mark VARCHAR(255), build VARCHAR(255),
build_k VARCHAR(255),
apartment VARCHAR(255),
telephone VARCHAR(255) NOT NULL,
CONSTRAINT fk_last_name FOREIGN KEY (last_name) REFERENCES last_name(last_name_id), CONSTRAINT fk_last_name FOREIGN KEY (last_name) REFERENCES last_name(last_name_id),
CONSTRAINT fk_first_name FOREIGN KEY (first_name) REFERENCES first_name(first_name_id), CONSTRAINT fk_first_name FOREIGN KEY (first_name) REFERENCES first_name(first_name_id),
CONSTRAINT fk_middle_name FOREIGN KEY (middle_name) REFERENCES middle_name(middle_name_id), CONSTRAINT fk_middle_name FOREIGN KEY (middle_name) REFERENCES middle_name(middle_name_id),
CONSTRAINT fk_discipline FOREIGN KEY (discipline) REFERENCES discipline(discipline_id), CONSTRAINT fk_street FOREIGN KEY (street) REFERENCES street(street_id),
PRIMARY KEY (uid) PRIMARY KEY (uid)
); );

View File

@ -4,31 +4,38 @@ import { Container, Dialog, Typography, IconButton, Tooltip } from '@mui/materia
import MUIDataTable from "mui-datatables"; import MUIDataTable from "mui-datatables";
import { useAxios } from './axios'; import { useAxios } from './axios';
import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useMutation, useQuery, useQueryClient } from 'react-query';
import StudentDialog from './StudentDialog'; import ContactDialog from './ContactDialog';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
function App() { function App() {
const client = useAxios(); const client = useAxios();
const queryClient = useQueryClient() const queryClient = useQueryClient()
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const [openedStudent, setOpenedStudent] = React.useState({}); const [openedContact, setOpenedContact] = React.useState({});
const { data, status } = useQuery('students', async () => { const { data, status } = useQuery('contacts', async () => {
const {data} = await client.get('/api/students'); const {data} = await client.get('/api/contacts');
return data.map(({ return data.map(({
uid, uid,
first_name, first_name,
last_name, last_name,
middle_name, middle_name,
discipline, telephone,
mark, street,
build,
build_k,
apartment
}) => { }) => {
return { return {
'first_name': first_name, 'first_name': first_name,
'last_name': last_name, 'last_name': last_name,
'middle_name': middle_name, 'middle_name': middle_name,
'discipline': discipline, 'telephone': telephone,
'mark': mark, 'street': street,
'build': build,
'build_k': build_k,
'apartment': apartment,
'display_name': `${first_name} ${last_name} ${middle_name}`, 'display_name': `${first_name} ${last_name} ${middle_name}`,
'display_address': `${street} ${build} ${build_k} ${apartment}`,
'uid': uid 'uid': uid
}; };
}) })
@ -43,23 +50,23 @@ function App() {
name: "display_name", label: "Имя" name: "display_name", label: "Имя"
}, },
{ {
name: "mark", label: "Оценка" name: "telephone", label: "Телефон"
}, },
{ {
name: "discipline", label: "Дисциплина" name: "display_address", label: "Адрес"
}, },
]; ];
const commitDelete = useMutation(async (uid) => { const commitDelete = useMutation(async (uid) => {
await client.delete(`/api/students/${uid}`); await client.delete(`/api/contacts/${uid}`);
}, { }, {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries('students'); queryClient.invalidateQueries('contacts');
} }
}); });
const handleRowClick = (rowData) => { const handleRowClick = (rowData) => {
setOpenedStudent(data.find(({uid}) => uid === rowData[0])); setOpenedContact(data.find(({uid}) => uid === rowData[0]));
setOpen(true); setOpen(true);
console.log(data.find(({uid}) => uid === rowData[0])); console.log(data.find(({uid}) => uid === rowData[0]));
}; };
@ -81,7 +88,7 @@ function App() {
return ( return (
<Container maxWidth="md"> <Container maxWidth="md">
<MUIDataTable <MUIDataTable
title={"Зачётная книжка"} title={"Контактная книга"}
data={data} data={data}
columns={columns} columns={columns}
options={{ options={{
@ -96,19 +103,19 @@ function App() {
onRowsDelete: handleRowsDelete onRowsDelete: handleRowsDelete
}} }}
/> />
<StudentDialog <ContactDialog
open={open} open={open}
onClose={handleClose} onClose={handleClose}
student={openedStudent} contact={openedContact}
/> />
</Container> </Container>
); );
function CustomToolbar() { function CustomToolbar() {
if (data !== undefined) return ( if (data !== undefined) return (
<Tooltip title='Новая запись'> <Tooltip title='Новый контакт'>
<IconButton onClick={() => { <IconButton onClick={() => {
setOpenedStudent({}); setOpenedContact({});
setOpen(true); setOpen(true);
}}> }}>
<AddIcon /> <AddIcon />

View File

@ -6,32 +6,35 @@ import { useAxios } from './axios';
import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useMutation, useQuery, useQueryClient } from 'react-query';
function StudentDialog({ function ContactDialog({
open, open,
onClose, onClose,
student, contact,
}) { }) {
const client = useAxios(); const client = useAxios();
const queryClient = useQueryClient() const queryClient = useQueryClient()
const [uid, setUid] = React.useState(student ? student.uid : null); const [uid, setUid] = React.useState(contact ? contact.uid : null);
const [firstName, setFirstName] = React.useState(student['first_name'] ?? ''); const [firstName, setFirstName] = React.useState(contact['first_name'] ?? '');
const [lastName, setLastName] = React.useState(student['last_name'] ?? ''); const [lastName, setLastName] = React.useState(contact['last_name'] ?? '');
const [middleName, setMiddleName] = React.useState(student['middle_name'] ?? ''); const [middleName, setMiddleName] = React.useState(contact['middle_name'] ?? '');
const [discipline, setDiscipline] = React.useState(student['discipline'] ?? ''); const [telephone, setTelephone] = React.useState(contact['telephone'] ?? '');
const [mark, setMark] = React.useState(student['mark'] ?? ''); const [street, setStreet] = React.useState(contact['street'] ?? '');
const [build, setBuild] = React.useState(contact['build'] ?? '');
const [buildK, setBuildK] = React.useState(contact['build_k'] ?? '');
const [apartment, setApartment] = React.useState(contact['apartment'] ?? '');
const commitCreate = useMutation(async (payload) => client.post('/api/students', payload), { const commitCreate = useMutation(async (payload) => client.post('/api/contacts', payload), {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries('students'); queryClient.invalidateQueries('contacts');
onClose(); onClose();
} }
}); });
const commitUpdate = useMutation(async (payload) => client.put(`/api/students/${student['uid']}`, payload), { const commitUpdate = useMutation(async (payload) => client.put(`/api/contacts/${contact['uid']}`, payload), {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries('students'); queryClient.invalidateQueries('contacts');
queryClient.invalidateQueries(['student', student['uid']]); queryClient.invalidateQueries(['contact', contact['uid']]);
onClose(); onClose();
} }
}); });
@ -42,10 +45,13 @@ function StudentDialog({
first_name: firstName, first_name: firstName,
last_name: lastName, last_name: lastName,
middle_name: middleName, middle_name: middleName,
discipline: discipline, telephone: telephone,
mark: mark, street: street,
build: build,
build_k: buildK,
apartment: apartment
}; };
if (student['uid']) { if (contact['uid']) {
commitUpdate.mutate(data); commitUpdate.mutate(data);
} }
else { else {
@ -55,13 +61,16 @@ function StudentDialog({
}; };
const loadData = () => { const loadData = () => {
console.log(student); console.log(contact);
setUid(student['uid'] ?? null); setUid(contact['uid'] ?? null);
setFirstName(student['first_name'] ?? ''); setFirstName(contact['first_name'] ?? '');
setLastName(student['last_name'] ?? ''); setLastName(contact['last_name'] ?? '');
setMiddleName(student['middle_name'] ?? ''); setMiddleName(contact['middle_name'] ?? '');
setDiscipline(student['discipline'] ?? ''); setTelephone(contact['telephone'] ?? '');
setMark(student['mark'] ?? ''); setStreet(contact['street'] ?? '');
setBuild(contact['build'] ?? '');
setBuildK(contact['build_k'] ?? '');
setApartment(contact['apartment'] ?? '');
}; };
return ( return (
@ -74,7 +83,7 @@ function StudentDialog({
onEnter: loadData onEnter: loadData
}} }}
> >
<DialogTitle>Студент</DialogTitle> <DialogTitle>Контакт</DialogTitle>
<form onSubmit={handleFormSubmit}> <form onSubmit={handleFormSubmit}>
<DialogContent dividers> <DialogContent dividers>
<Grid container spacing={3}> <Grid container spacing={3}>
@ -107,20 +116,47 @@ function StudentDialog({
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
id="discipline" id="telephone"
label="Дисциплина" label="Телефон"
value={discipline} value={telephone}
onChange={(event) => setDiscipline(event.target.value)} onChange={(event) => setTelephone(event.target.value)}
fullWidth fullWidth
required required
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={6}>
<TextField <TextField
id="mark" id="street"
label="Оценка" label="Улица"
value={mark} value={street}
onChange={(event) => setMark(event.target.value)} onChange={(event) => setStreet(event.target.value)}
fullWidth
/>
</Grid>
<Grid item xs={2}>
<TextField
id="build"
label="Дом"
value={build}
onChange={(event) => setBuild(event.target.value)}
fullWidth
/>
</Grid>
<Grid item xs={2}>
<TextField
id="build_k"
label="Корпус"
value={buildK}
onChange={(event) => setBuildK(event.target.value)}
fullWidth
/>
</Grid>
<Grid item xs={2}>
<TextField
id="apartment"
label="Квартира"
value={apartment}
onChange={(event) => setApartment(event.target.value)}
fullWidth fullWidth
/> />
</Grid> </Grid>
@ -136,4 +172,4 @@ function StudentDialog({
</Dialog> </Dialog>
); );
} }
export default StudentDialog; export default ContactDialog;

View File

@ -1,7 +1,5 @@
flask flask
flask_restful flask_restful
flask_socketio flask_socketio
flask_cors
pytz pytz
click
psycopg2 psycopg2