update a few things
This commit is contained in:
+4
-1
@@ -9,4 +9,7 @@ no nights.log
|
|||||||
no twighlights.log
|
no twighlights.log
|
||||||
no weekends.log
|
no weekends.log
|
||||||
logs/
|
logs/
|
||||||
.env/
|
.env/
|
||||||
|
|
||||||
|
*.db
|
||||||
|
web/rota
|
||||||
|
|||||||
Vendored
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
"name": "Python: Run",
|
"name": "Python: Run",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/run.py",
|
"program": "${workspaceFolder}/gen.py",
|
||||||
"console": "integratedTerminal"
|
"console": "integratedTerminal"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
+25
-20
@@ -286,7 +286,7 @@ class RotaBuilder(object):
|
|||||||
):
|
):
|
||||||
console.print("Setting up solver")
|
console.print("Setting up solver")
|
||||||
|
|
||||||
solver = "scip"
|
#solver = "scip"
|
||||||
|
|
||||||
if solver == "scip":
|
if solver == "scip":
|
||||||
self.opt = SolverFactory(solver, executable="scip")
|
self.opt = SolverFactory(solver, executable="scip")
|
||||||
@@ -295,23 +295,28 @@ class RotaBuilder(object):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.print("Solving")
|
try:
|
||||||
if use_neos:
|
console.print("Solving")
|
||||||
solver_manager = SolverManagerFactory("neos") # Solve using neos server
|
if use_neos:
|
||||||
# results = solver_manager.solve(Rota.model, opt=opt, logfile="test.log")
|
solver_manager = SolverManagerFactory("neos") # Solve using neos server
|
||||||
results = solver_manager.solve(
|
# results = solver_manager.solve(Rota.model, opt=opt, logfile="test.log")
|
||||||
self.model, keepfiles=True, tee=True, opt=self.opt, logfile="test.log"
|
results = solver_manager.solve(
|
||||||
)
|
self.model, keepfiles=True, tee=True, opt=self.opt, logfile="test.log"
|
||||||
else:
|
)
|
||||||
results = self.opt.solve(
|
else:
|
||||||
self.model,
|
results = self.opt.solve(
|
||||||
tee=True,
|
self.model,
|
||||||
options=options,
|
tee=True,
|
||||||
# options={
|
options=options,
|
||||||
# "threads": 10,
|
# options={
|
||||||
# },
|
# "threads": 10,
|
||||||
logfile="test.log",
|
# },
|
||||||
)
|
logfile="test.log",
|
||||||
|
keepfiles=True,
|
||||||
|
)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
return
|
||||||
|
pass
|
||||||
|
|
||||||
self.results = results
|
self.results = results
|
||||||
|
|
||||||
@@ -321,8 +326,8 @@ class RotaBuilder(object):
|
|||||||
console.print(f"Attempting each shift individually")
|
console.print(f"Attempting each shift individually")
|
||||||
self.solve_shifts_individually(options)
|
self.solve_shifts_individually(options)
|
||||||
|
|
||||||
if not results.solver.status:
|
#if not results.solver.status:
|
||||||
sys.exit(0)
|
# sys.exit(0)
|
||||||
|
|
||||||
def solve_shifts_by_block(self, options, block_length: int, folder="block_shifts"):
|
def solve_shifts_by_block(self, options, block_length: int, folder="block_shifts"):
|
||||||
shifts = self.shifts
|
shifts = self.shifts
|
||||||
|
|||||||
+1
-1
@@ -77,7 +77,7 @@ class Worker(BaseModel):
|
|||||||
not_available_to_work: list[NotAvailableToWork] = []
|
not_available_to_work: list[NotAvailableToWork] = []
|
||||||
pref_not_to_work: list[PreferenceNotToWork] = []
|
pref_not_to_work: list[PreferenceNotToWork] = []
|
||||||
work_requests: list[WorkRequests] = []
|
work_requests: list[WorkRequests] = []
|
||||||
remote_site: str = ("plymouth",) # We set a default proc_site
|
remote_site: str = "plymouth" # We set a default proc_site
|
||||||
previous_shifts: dict = {}
|
previous_shifts: dict = {}
|
||||||
shift_balance_extra: dict = {}
|
shift_balance_extra: dict = {}
|
||||||
bank_holiday_extra: int = 0
|
bank_holiday_extra: int = 0
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
parallel/minnthreads = 5
|
#limits/gap = 0.005
|
||||||
parallel/maxnthreads = 10
|
|
||||||
|
|
||||||
limits/solutions = -1
|
#limits/time = 100
|
||||||
limits/gap = 0
|
|
||||||
|
|
||||||
limits/time = 20000
|
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
import models, schemas
|
||||||
|
|
||||||
|
|
||||||
|
def get_shift(db: Session, shift_id: int):
|
||||||
|
return (
|
||||||
|
db.query(models.SingleShift).filter(models.SingleShift.id == shift_id).first()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_shift_by_name(db: Session, name: str):
|
||||||
|
return db.query(models.SingleShift).filter(models.SingleShift.name == name).first()
|
||||||
|
|
||||||
|
|
||||||
|
def get_shifts(db: Session, skip: int = 0, limit: int = 100):
|
||||||
|
return db.query(models.SingleShift).offset(skip).limit(limit).all()
|
||||||
|
|
||||||
|
|
||||||
|
def create_shift(db: Session, shift: schemas.SingleShiftCreate):
|
||||||
|
shift = models.SingleShift(**shift.dict())
|
||||||
|
db.add(shift)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(shift)
|
||||||
|
return shift
|
||||||
|
|
||||||
|
def create_worker(db: Session, worker: schemas.WorkerCreate):
|
||||||
|
worker = models.Worker(**worker.dict())
|
||||||
|
db.add(worker)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(worker)
|
||||||
|
return worker
|
||||||
|
|
||||||
|
def get_worker(db: Session, worker_id: int):
|
||||||
|
return (
|
||||||
|
db.query(models.Worker).filter(models.Worker.id == worker_id).first()
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_worker_by_name(db: Session, name: str):
|
||||||
|
return db.query(models.Worker).filter(models.Worker.name == name).first()
|
||||||
|
|
||||||
|
def get_workers(db: Session, skip: int = 0, limit: int = 100):
|
||||||
|
return db.query(models.Worker).offset(skip).limit(limit).all()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#def get_shift_constraints(db: Session, skip: int = 0, limit: int = 100):
|
||||||
|
# return db.query(models.ShiftConstraint).offset(skip).limit(limit).all()
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#def create_shift_constraints(
|
||||||
|
# db: Session, item: schemas.ShiftConstraintCreate, constraint_id: int
|
||||||
|
#):
|
||||||
|
# db_item = models.ShiftConstraint(**item.dict(), constraint_id=constraint_id)
|
||||||
|
# db.add(db_item)
|
||||||
|
# db.commit()
|
||||||
|
# db.refresh(db_item)
|
||||||
|
# return db_item
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
|
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
|
||||||
|
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
||||||
|
)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
+87
-14
@@ -1,16 +1,21 @@
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI, Query
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from fastapi_sqlalchemy import DBSessionMiddleware, db
|
from fastapi_sqlalchemy import DBSessionMiddleware, db
|
||||||
|
from fastapi import Depends, FastAPI, HTTPException
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from rota.shifts import SingleShift, ShiftConstraint
|
from rota.shifts import SingleShift, ShiftConstraint
|
||||||
|
|
||||||
from web.models import SingleShift as ModelSingleShift
|
from models import SingleShift as ModelSingleShift
|
||||||
from web.models import ShiftConstraint as ModelShiftConstraint
|
#from models import ShiftConstraint as ModelShiftConstraint
|
||||||
|
|
||||||
|
from database import SessionLocal, engine
|
||||||
|
|
||||||
|
import crud, models, schemas
|
||||||
|
|
||||||
#from rota.wokers
|
#from rota.wokers
|
||||||
|
|
||||||
@@ -19,21 +24,89 @@ load_dotenv('../.env')
|
|||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
app.add_middleware(DBSessionMiddleware, db_url="sqlite:///test.db")
|
# Dependency
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
@app.post('/shifts/', response_model=SingleShift)
|
|
||||||
async def shifts(shift: ModelSingleShift):
|
#app.add_middleware(DBSessionMiddleware, db_url="sqlite:///test.db")
|
||||||
db_shift = ModelSingleShift(name=shift.name, length=shift.length)
|
models.Base.metadata.create_all(bind=engine)
|
||||||
db.session.add(db_shift)
|
|
||||||
db.session.commit()
|
@app.post("/shifts/", response_model=schemas.SingleShift)
|
||||||
|
def create_shift(shift: schemas.SingleShiftCreate, db: Session = Depends(get_db)):
|
||||||
|
db_shift = crud.get_shift_by_name(db, name=shift.name)
|
||||||
|
if db_shift:
|
||||||
|
raise HTTPException(status_code=400, detail="Shift name already exists")
|
||||||
|
return crud.create_shift(db=db, shift=shift)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/shifts/", response_model=list[schemas.SingleShift])
|
||||||
|
def read_shifts(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||||
|
shifts = crud.get_shifts(db, skip=skip, limit=limit)
|
||||||
|
return shifts
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/shifts/{shift_id}", response_model=schemas.SingleShift)
|
||||||
|
def read_shift(shift_id: int, db: Session = Depends(get_db)):
|
||||||
|
db_shift = crud.get_shifts(db, shift_id=shift_id)
|
||||||
|
if db_shift is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Shift not found")
|
||||||
return db_shift
|
return db_shift
|
||||||
|
|
||||||
|
@app.post("/workers/", response_model=schemas.Worker)
|
||||||
@app.get("/shifts/")
|
def create_worker(worker: schemas.WorkerCreate, db: Session = Depends(get_db)):
|
||||||
async def shifts():
|
db_worker = crud.get_worker_by_name(db, name=worker.name)
|
||||||
shifts = db.session.query(ModelSingleShift).all()
|
if db_worker:
|
||||||
|
raise HTTPException(status_code=400, detail="worker name already exists")
|
||||||
|
return crud.create_worker(db=db, worker=worker)
|
||||||
|
|
||||||
return shifts
|
|
||||||
|
@app.get("/workers/", response_model=list[schemas.Worker])
|
||||||
|
def read_workers(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||||
|
workers = crud.get_workers(db, skip=skip, limit=limit)
|
||||||
|
return workers
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/workers/{worker_id}", response_model=schemas.Worker)
|
||||||
|
def read_worker(worker_id: int, db: Session = Depends(get_db)):
|
||||||
|
db_worker = crud.get_workers(db, worker_id=worker_id)
|
||||||
|
if db_worker is None:
|
||||||
|
raise HTTPException(status_code=404, detail="worker not found")
|
||||||
|
return db_worker
|
||||||
|
|
||||||
|
#@app.post("/users/{user_id}/items/", response_model=schemas.Item)
|
||||||
|
#def create_item_for_user(
|
||||||
|
# user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
|
||||||
|
#):
|
||||||
|
# return crud.create_user_item(db=db, item=item, user_id=user_id)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#@app.get("/items/", response_model=list[schemas.Item])
|
||||||
|
#def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||||
|
# items = crud.get_items(db, skip=skip, limit=limit)
|
||||||
|
# return items
|
||||||
|
|
||||||
|
#@app.post("/shifts/")
|
||||||
|
#async def create_shift(shift: SingleShift):
|
||||||
|
# return shift
|
||||||
|
|
||||||
|
#@app.post('/shifts/', response_model=SingleShift)
|
||||||
|
#async def shifts(shift: ModelSingleShift):
|
||||||
|
# db_shift = ModelSingleShift(name=shift.name, length=shift.length)
|
||||||
|
# db.session.add(db_shift)
|
||||||
|
# db.session.commit()
|
||||||
|
# return db_shift
|
||||||
|
|
||||||
|
|
||||||
|
#@app.get("/shifts/")
|
||||||
|
#async def shifts():
|
||||||
|
# shifts = db.session.query(ModelSingleShift).all()
|
||||||
|
#
|
||||||
|
# return shifts
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
|||||||
+65
-12
@@ -1,31 +1,84 @@
|
|||||||
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Float, Boolean
|
from sqlalchemy import (
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
Column,
|
||||||
|
DateTime,
|
||||||
|
ForeignKey,
|
||||||
|
Integer,
|
||||||
|
String,
|
||||||
|
Float,
|
||||||
|
Boolean,
|
||||||
|
JSON,
|
||||||
|
)
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
Base = declarative_base()
|
from database import Base
|
||||||
|
|
||||||
|
class Worker(Base):
|
||||||
|
__tablename__ = "worker"
|
||||||
|
name= Column(String)
|
||||||
|
site= Column(String)
|
||||||
|
grade= Column(Integer)
|
||||||
|
id=Column(Integer, primary_key=True, index=True)
|
||||||
|
fte= Column(Integer)
|
||||||
|
nwds= Column(JSON)
|
||||||
|
start_date= Column(DateTime)
|
||||||
|
end_date= Column(DateTime)
|
||||||
|
oop= Column(JSON)
|
||||||
|
not_available_to_work= Column(JSON)
|
||||||
|
pref_not_to_work= Column(JSON)
|
||||||
|
work_requests= Column(JSON)
|
||||||
|
remote_site= Column(String)
|
||||||
|
previous_shifts= Column(JSON)
|
||||||
|
shift_balance_extra=Column(JSON)
|
||||||
|
bank_holiday_extra= Column(Integer)
|
||||||
|
pair= Column(String)
|
||||||
|
|
||||||
class SingleShift(Base):
|
class SingleShift(Base):
|
||||||
__tablename__ = 'single_shift'
|
__tablename__ = "single_shift"
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
name = Column(String)
|
name = Column(String)
|
||||||
length = Column(Float)
|
length = Column(Float)
|
||||||
|
days = Column(JSON)
|
||||||
|
sites = Column(JSON)
|
||||||
balance_offset = Column(Float, nullable=True)
|
balance_offset = Column(Float, nullable=True)
|
||||||
balance_weighting = Column(Float)
|
balance_weighting = Column(Float)
|
||||||
workers_required = Column(Float)
|
workers_required = Column(Float)
|
||||||
|
rota_on_nwds = Column(Boolean)
|
||||||
assign_as_block = Column(Boolean)
|
assign_as_block = Column(Boolean)
|
||||||
force_as_block = Column(Boolean)
|
force_as_block = Column(Boolean)
|
||||||
|
force_as_block_unless_nwd = Column(Boolean)
|
||||||
hard_constrain_shift = Column(Boolean)
|
hard_constrain_shift = Column(Boolean)
|
||||||
bank_holidays_only = Column(Boolean)
|
bank_holidays_only = Column(Boolean)
|
||||||
constraint_id = Column(Integer, ForeignKey('shift_constraint.id'))
|
|
||||||
|
|
||||||
constraint = relationship('ShiftConstraint')
|
#constraint = relationship("ShiftConstraint", back_populates="shifts")
|
||||||
|
constraint = Column(JSON)
|
||||||
|
|
||||||
|
# This should never be used from here (automatically created by the pydantic model)
|
||||||
|
constraint_options = Column(JSON)
|
||||||
|
constraints = Column(JSON)
|
||||||
|
|
||||||
|
#sites = relationship("Site", secondary="site_shift_link_table")
|
||||||
|
|
||||||
|
#class ShiftConstraint(Base):
|
||||||
|
# __tablename__ = "shift_constraint"
|
||||||
|
# id = Column(Integer, primary_key=True)
|
||||||
|
# name = Column(String)
|
||||||
|
# options = Column(Integer, nullable=True)
|
||||||
|
#
|
||||||
|
# shifts = relationship("SingleShift", back_populates="constraint")
|
||||||
|
#
|
||||||
|
# shift_id = Column(Integer, ForeignKey("single_shift.id"))
|
||||||
|
|
||||||
|
|
||||||
class ShiftConstraint(Base):
|
#class SiteShiftLinkTable(Base):
|
||||||
__tablename__ = 'shift_constraint'
|
# __tablename__ = "site_shift_link_table"
|
||||||
id = Column(Integer, primary_key=True)
|
# shift_id = Column(Integer, ForeignKey("single_shift.id"), primary_key=True)
|
||||||
name = Column(String)
|
# site_id = Column(Integer, ForeignKey("sites.id"), primary_key=True)
|
||||||
options = Column(Integer, nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
|
#class Site(Base):
|
||||||
|
# __tablename__ = "sites"
|
||||||
|
# id = Column(Integer, primary_key=True)
|
||||||
|
# name = Column(String)
|
||||||
|
#
|
||||||
|
# shifts = relationship(SingleShift, secondary="site_shift_link_table")
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
from rota.shifts import SingleShift as SingleShiftBase, ShiftConstraint as ShiftConstraintBase
|
||||||
|
from rota.workers import Worker as WorkerBase
|
||||||
|
|
||||||
|
|
||||||
|
class SingleShift(SingleShiftBase):
|
||||||
|
|
||||||
|
id: int
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
class SingleShiftCreate(SingleShiftBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#class ShiftConstraint(ShiftConstraintBase):
|
||||||
|
# id: int
|
||||||
|
#
|
||||||
|
# class Config:
|
||||||
|
# orm_mode = True
|
||||||
|
#
|
||||||
|
#class ShiftConstraintCreate(ShiftConstraintBase):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
class Worker(WorkerBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
class WorkerCreate(WorkerBase):
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user