Guest Book

Chore:
  Added New Day Offset and Timezone Offset in settings so that they can be shared consistently and also set in runtime.
This commit is contained in:
Amritanshu Agrawal 2020-09-22 10:53:31 +05:30
parent cabdd505e0
commit b19d8cc030
13 changed files with 223 additions and 75 deletions

View File

@ -8,8 +8,11 @@ from pydantic import BaseSettings, PostgresDsn, validator
class Settings(BaseSettings):
# openssl rand -hex 32
SECRET_KEY: str = secrets.token_urlsafe(32)
MIDDLEWARE_SECRET_KEY: str = secrets.token_urlsafe(5)
ALGORITHM: str = "HS256"
JWT_TOKEN_EXPIRE_MINUTES: int = 30
NEW_DAY_OFFSET_MINUTES: int = 7 * 60
TIMEZONE_OFFSET_MINUTES: int = 330
HOST: str = "0.0.0.0"
PORT: int = 80
DEBUG: bool = False

View File

@ -37,11 +37,16 @@ class GuestBook(Base):
customer = relationship("Customer")
def __init__(self, customer_id=None, pax=None, id_=None):
def __init__(self, pax=None, id_=None, customer_id=None, customer=None):
self.customer_id = customer_id
self.pax = pax
self.id = id_
self.date = datetime.utcnow()
if customer is None:
self.customer_id = customer_id
else:
self.customer = customer
class Overview(Base):

View File

@ -1,69 +1,126 @@
import uuid
from datetime import datetime, date, timedelta
from typing import Optional
from datetime import date, timedelta, datetime
from fastapi import APIRouter, HTTPException, status, Depends, Security
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
import transaction
from pyramid.view import view_config
from ..core.config import settings
from ..schemas.auth import UserToken
import barker.schemas.master as schemas
from ..core.security import get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.master import Customer
from ..models.voucher import GuestBook
from barker.models import GuestBook, Customer
router = APIRouter()
@view_config(
request_method="PUT", route_name="v1_guest_book_new", renderer="json", permission="Guest Book", trans=True,
)
def save(request):
json = request.json_body
company = json["company"].strip() if json["company"] is not None else ""
name = json["name"].strip()
phone = json["phone"]
address = json["address"].strip()
customer = request.dbsession.query(Customer).filter(Customer.phone == phone).first()
if customer is None:
customer = Customer(company, name, phone, address)
request.dbsession.add(customer)
else:
if company != "":
customer.company = company
if name != "":
customer.name = name
if address != "":
customer.address = address
item = GuestBook(pax=json["pax"])
item.customer = customer
request.dbsession.add(item)
transaction.commit()
item = request.dbsession.query(GuestBook).filter(GuestBook.id == item.id).first()
return guest_book_info(item)
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@view_config(
request_method="GET", route_name="v1_guest_book_id", renderer="json", permission="Guest Book",
)
def show_id(request):
id_ = request.matchdict["id"]
item = request.dbsession.query(GuestBook).filter(GuestBook.id == uuid.UUID(id_)).one()
return guest_book_info(item)
@router.post("", response_model=schemas.GuestBook)
def save(
data: schemas.GuestBookIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["guest-book"]),
):
try:
customer: Customer = db.query(Customer).filter(Customer.phone == data.phone).first()
if customer is None:
customer = Customer(company=data.company, name=data.name, phone=data.phone, address=data.address,)
db.add(customer)
else:
customer.name = data.name or customer.name
customer.company = data.company or customer.company
customer.address = data.address or customer.address
item = GuestBook(pax=data.pax, customer=customer)
db.add(item)
db.commit()
return guest_book_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
)
except Exception:
db.rollback()
raise
@view_config(
request_method="GET", route_name="v1_guest_book_new", renderer="json", permission="Guest Book",
)
def show_blank(request):
@router.put("/{id_}", response_model=schemas.GuestBook)
def update(
id_: uuid.UUID,
data: schemas.GuestBookIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["guest-book"]),
):
try:
item: GuestBook = db.query(GuestBook).filter(GuestBook.id == id_).first()
item.customer.company = data.company
item.customer.name = data.name
item.customer.phone = data.phone
item.customer.address = data.address
item.pax = data.pax
db.commit()
return guest_book_info(item)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
)
except Exception:
db.rollback()
raise
@router.delete("/{id_}")
def delete(
id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["guest-book"]),
):
try:
item: GuestBook = db.query(GuestBook).filter(GuestBook.id == id_).first()
db.delete(item)
db.commit()
return guest_book_info(None)
except SQLAlchemyError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
)
except Exception:
db.rollback()
raise
@router.get("")
def show_blank(
db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["guest-book"]),
):
return guest_book_info(None)
@view_config(
request_method="GET", route_name="v1_guest_book_list", renderer="json", permission="Authenticated",
)
def show_list(request):
date_ = request.GET.get("q", None)
if date_ is None or date_ == "":
date_ = date.today().strftime("%d-%b-%Y")
@router.get("/list")
def show_list(q: Optional[str] = None, db: Session = Depends(get_db), user: UserToken = Depends(get_user)):
if q is None or q == "":
q = date.today()
else:
q = datetime.strptime(q, "%d-%b-%Y")
list_ = (
request.dbsession.query(GuestBook)
db.query(GuestBook)
.filter(
GuestBook.date >= datetime.strptime(date_, "%d-%b-%Y") - timedelta(minutes=30) # hack for timezone and 5 am
GuestBook.date >= q + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES)
)
.filter(
GuestBook.date
< q + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES - settings.TIMEZONE_OFFSET_MINUTES, days=1)
)
.filter(GuestBook.date < datetime.strptime(date_, "%d-%b-%Y") - timedelta(minutes=30) + timedelta(days=1))
.order_by(GuestBook.date)
.all()
)
@ -79,13 +136,22 @@ def show_list(request):
"name": item.customer.name,
"phone": item.customer.phone,
"pax": item.pax,
"date": item.date.strftime("%d-%b-%Y %H:%M"),
"status": "" if item.status is None else item.status.status,
},
)
return {"date": date_, "list": guest_book}
return {"date": q.strftime("%d-%b-%Y"), "list": guest_book}
def guest_book_info(item):
@router.get("/{id_}")
def show_id(
id_: uuid.UUID, db: Session = Depends(get_db), user: UserToken = Security(get_user, scopes=["guest-book"]),
):
item: GuestBook = db.query(GuestBook).filter(GuestBook.id == id_).first()
return guest_book_info(item)
def guest_book_info(item: Optional[GuestBook]):
if item is not None:
return {
"id": item.id,
@ -94,6 +160,7 @@ def guest_book_info(item):
"phone": item.customer.phone,
"pax": item.pax,
"address": item.customer.address,
"date": item.date.strftime("%d-%b-%Y %H:%M"),
}
else:
return {"company": "", "name": "", "phone": "", "pax": 0, "address": ""}

View File

@ -0,0 +1,25 @@
import uuid
from pydantic import BaseModel, Field
from barker.schemas import to_camel
from barker.schemas.customer import CustomerIn
class GuestBookIn(CustomerIn):
pax: int = Field(ge=0)
class Config:
fields = {"id_": "id"}
anystr_strip_whitespace = True
alias_generator = to_camel
class GuestBook(GuestBookIn):
id_: uuid.UUID
date: str
class Config:
fields = {"id_": "id"}
anystr_strip_whitespace = True
alias_generator = to_camel

View File

@ -9,6 +9,7 @@ from barker.schemas import to_camel
from barker.schemas.tax import TaxLink, TaxIn, Tax # noqa: F401
from barker.schemas.sale_category import SaleCategoryLink, SaleCategoryIn, SaleCategory # noqa: F401
from barker.schemas.customer import CustomerIn, Customer # noqa: F401
from barker.schemas.guest_book import GuestBookIn, GuestBook # noqa: F401
from barker.schemas.menu_category import MenuCategoryLink, MenuCategoryIn, MenuCategory # noqa: F401
from barker.schemas.product import ProductLink, ProductIn, Product # noqa: F401
from barker.schemas.modifier_category import ModifierCategoryLink, ModifierCategoryIn, ModifierCategory # noqa: F401

View File

@ -25,7 +25,7 @@ import { CoreModule } from './core/core.module';
import { ReactiveFormsModule } from '@angular/forms';
import { SharedModule } from './shared/shared.module';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatToolbarModule } from "@angular/material/toolbar";
import { MatToolbarModule } from '@angular/material/toolbar';
import { NavBarComponent } from './nav-bar/nav-bar.component';
registerLocaleData(enIN);

View File

@ -43,6 +43,12 @@
<mat-cell *matCellDef="let row">{{row.pax}}</mat-cell>
</ng-container>
<!-- Time Column -->
<ng-container matColumnDef="date">
<mat-header-cell *matHeaderCellDef>Time</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.date | localTime}}</mat-cell>
</ng-container>
<!-- Action Column -->
<ng-container matColumnDef="action">
<mat-header-cell *matHeaderCellDef class="center">Action</mat-header-cell>
@ -53,7 +59,7 @@
<button mat-icon-button [routerLink]="['/guest-book/', row.id]">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button color="warn" (click)="deleteRow(row)">
<button mat-icon-button color="warn" (click)="confirmDelete(row.id)">
<mat-icon>delete</mat-icon>
</button>
</mat-cell>

View File

@ -1,10 +1,13 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { FormBuilder, FormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import * as moment from 'moment';
import {GuestBook, GuestBookList} from '../guest-book';
import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component';
import { ToasterService } from '../../core/toaster.service';
import { GuestBook, GuestBookList } from '../guest-book';
import { GuestBookService } from '../guest-book.service';
import { GuestBookListDataSource } from './guest-book-list-datasource';
@ -18,9 +21,16 @@ export class GuestBookListComponent implements OnInit {
form: FormGroup;
data: BehaviorSubject<GuestBook[]>;
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns = ['sno', 'name', 'phone', 'pax', 'action'];
displayedColumns = ['sno', 'name', 'phone', 'pax', 'date', 'action'];
constructor(private route: ActivatedRoute, private fb: FormBuilder, private ser: GuestBookService) {
constructor(
private route: ActivatedRoute,
private router: Router,
private fb: FormBuilder,
private dialog: MatDialog,
private toaster: ToasterService,
private ser: GuestBookService
) {
this.createForm();
this.data = new BehaviorSubject([]);
this.listenToDateChange();
@ -51,4 +61,32 @@ export class GuestBookListComponent implements OnInit {
});
this.dataSource = new GuestBookListDataSource(this.data);
}
delete(id: string) {
this.ser.delete(id)
.subscribe(
(result) => {
this.toaster.show('Success', '');
this.router.navigateByUrl('/guest-book');
},
(error) => {
this.toaster.show('Danger', error);
}
);
}
confirmDelete(id: string): void {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: '250px',
data: {title: 'Delete Guest Book Entry?', content: 'Are you sure? This cannot be undone.'}
});
dialogRef.afterClosed().subscribe((result: boolean) => {
if (result) {
this.delete(id);
}
});
}
}

View File

@ -16,7 +16,8 @@ const routes: Routes = [
},
resolve: {
list: GuestBookListResolver
}
},
runGuardsAndResolvers: 'always'
},
{
path: 'new',

View File

@ -15,6 +15,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
import { GuestBookDetailComponent } from './guest-book-detail/guest-book-detail.component';
import { GuestBookListComponent } from './guest-book-list/guest-book-list.component';
import { GuestBookRoutingModule } from './guest-book-routing.module';
import { SharedModule } from '../shared/shared.module';
export const MY_FORMATS = {
parse: {
@ -43,7 +44,8 @@ export const MY_FORMATS = {
MatNativeDateModule,
ReactiveFormsModule,
FlexLayoutModule,
GuestBookRoutingModule
GuestBookRoutingModule,
SharedModule
],
providers: [
{provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE]},

View File

@ -9,7 +9,7 @@ const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const url = '/v1/guest-book';
const url = '/api/guest-book';
const serviceName = 'GuestBookService';
@Injectable({providedIn: 'root'})
@ -19,7 +19,7 @@ export class GuestBookService {
}
get(id: string): Observable<GuestBook> {
const getUrl: string = (id === null) ? `${url}/new` : `${url}/${id}`;
const getUrl: string = (id === null) ? url : `${url}/${id}`;
return <Observable<GuestBook>>this.http.get<GuestBook>(getUrl)
.pipe(
catchError(this.log.handleError(serviceName, `get id=${id}`))
@ -28,22 +28,21 @@ export class GuestBookService {
list(date: string): Observable<GuestBookList> {
const options = {params: new HttpParams().set('q', (date === null) ? '' : date)};
const listUrl: string = url;
return <Observable<GuestBookList>>this.http.get<GuestBookList>(listUrl, options)
return <Observable<GuestBookList>>this.http.get<GuestBookList>(`${url}/list`, options)
.pipe(
catchError(this.log.handleError(serviceName, 'list'))
);
}
save(guestBook: GuestBook): Observable<GuestBook> {
return <Observable<GuestBook>>this.http.put<GuestBook>(`${url}/new`, guestBook, httpOptions)
return <Observable<GuestBook>>this.http.post<GuestBook>(url, guestBook, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'save'))
);
}
update(guestBook: GuestBook): Observable<GuestBook> {
return <Observable<GuestBook>>this.http.post<GuestBook>(`${url}/${guestBook.id}`, guestBook, httpOptions)
return <Observable<GuestBook>>this.http.put<GuestBook>(`${url}/${guestBook.id}`, guestBook, httpOptions)
.pipe(
catchError(this.log.handleError(serviceName, 'update'))
);

View File

@ -6,6 +6,7 @@ export class GuestBook {
phone: string;
pax: number;
address: string;
date: string;
public constructor(init?: Partial<GuestBook>) {
Object.assign(this, init);

View File

@ -1,12 +1,12 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ConfirmDialogComponent} from './confirm-dialog/confirm-dialog.component';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import {LocalTimePipe} from './local-time.pipe';
import {ClearPipe} from './clear.pipe';
import {AccountingPipe} from './accounting.pipe';
import {ImageDialogComponent} from './image-dialog/image-dialog.component';
import { LocalTimePipe } from './local-time.pipe';
import { ClearPipe } from './clear.pipe';
import { AccountingPipe } from './accounting.pipe';
import { ImageDialogComponent } from './image-dialog/image-dialog.component';
@NgModule({
imports: [