Skip to content

Commit

Permalink
Generate demo data
Browse files Browse the repository at this point in the history
  • Loading branch information
valasek committed Sep 23, 2019
1 parent 4870aa8 commit 2709f1e
Show file tree
Hide file tree
Showing 19 changed files with 294 additions and 54 deletions.
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"chartjs-plugin-datalabels": "^0.7.0",
"date-fns": "^2.2.1",
"date-fns-tz": "^1.0.7",
"quasar": "^1.1.3",
"quasar": "^1.1.4",
"vue-chartjs": "^3.4.2"
},
"devDependencies": {
Expand Down
Binary file modified client/src/statics/administration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9281,10 +9281,10 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==

quasar@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/quasar/-/quasar-1.1.3.tgz#f862d24f1b83f00b5ea93a25711cb542899c508d"
integrity sha512-mqsaLORO+2eUHTEJAp0qMzJ3XdgRWkmniauTdPmGhx9i6GvmJK4KN1zmyzSHzoJ9OVX4N8aGKAkjMlsAPztODQ==
quasar@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/quasar/-/quasar-1.1.4.tgz#7323c713a2f3f7b2085632cbce50b1246fe37551"
integrity sha512-A6SI9vQNFBQ10DjAeSAVtvARbdyZ5uHE5ecGTreMCuKULehWnsJEsRmohM3GU4SttINesONCN99D3Lek10nwRA==

query-string@^4.1.0:
version "4.3.4"
Expand Down
Binary file removed screenshots/administration.png
Binary file not shown.
Binary file removed screenshots/help.png
Binary file not shown.
Binary file removed screenshots/holidays.png
Binary file not shown.
Binary file removed screenshots/home.png
Binary file not shown.
Binary file removed screenshots/reported_overview.png
Binary file not shown.
43 changes: 38 additions & 5 deletions server/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ type AppSettings struct {

// EntityOverview -
type EntityOverview struct {
Name string `json:"name"`
Total int `json:"total"`
Active int `json:"active"`
Disabled int `json:"disabled"`
Deleted int `json:"deleted"`
Name string `json:"name"`
Total int `json:"total"`
Active int `json:"active"`
Disabled int `json:"disabled"`
Deleted int `json:"deleted"`
}

// AppSettings returns list of all appliocation and user settings for configuration file
Expand Down Expand Up @@ -339,6 +339,39 @@ func ResetAPI(db *models.DB) {
logger.Log.Info("- holidays")
}

// GenerateAPI - generates demo initial data and saves them into ./data folder
func GenerateAPI(folder string, db *models.DB) {

api := NewAPI(db)
var err error

logger.Log.Infof("generated files (%s):", folder)
tableNames := []string{"rates", "projects", "reported_records", "consultants", "holidays"}
for _, baseFileName := range tableNames {

fileName := baseFileName + ".csv"
filePath := filepath.Join(folder, fileName)
n := 0
switch baseFileName {
case "projects":
n, err = api.projects.ProjectGenerate(filePath)
case "rates":
n, err = api.rates.RateGenerate(filePath)
case "consultants":
n, err = api.consultants.ConsultantGenerate(filePath)
case "holidays":
n, err = api.holidays.HolidayGenerate(filePath)
case "reported_records":
n, err = api.reportedRecords.ReportedRecordGenerate(filePath)
}
if err != nil {
logger.Log.Error(fmt.Sprintf("generated tables: error during %s generate: %s", baseFileName, err))
} else {
logger.Log.Info(fmt.Sprintf("- %s.csv, %d records", baseFileName, n))
}
}
}

// SeedAPI - loads initial data into DB
func SeedAPI(db *models.DB, table string, inputFiles map[string]string) error {
api := NewAPI(db)
Expand Down
1 change: 0 additions & 1 deletion server/api/reportedRecords.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ func (api *API) ReportedRecordsInPeriod(c *gin.Context) {
c.JSON(http.StatusOK, reportedRecords)
}


// ReportedRecordsSummary returns list of all
func (api *API) ReportedRecordsSummary(c *gin.Context) {
year := c.Param("year")
Expand Down
32 changes: 19 additions & 13 deletions server/cmd/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,37 @@ import (
)

var (
load string
clean bool
backup bool
load string
generate bool
clean bool
backup bool
)

// dbCmd represents the db command
var dbCmd = &cobra.Command{
Use: "db",
Short: "Initiate, load or backup DB. See timesheet help db",
Long: `Initiate, load, or backup DB.
Command first tests connection to DB. If succeeds it will initiate, load or backup db and exit.`,
Short: "Initiate, load. backup DB og generate demo data. See timesheet help db",
Long: `Initiate, load, backup DB or generate demo data.
Command first tests connection to DB. If succeeds it will initiate, load, backup db or generate demo data and exit.`,
Run: func(cmd *cobra.Command, args []string) {
db := api.ConnectDB()
defer db.Close()
if clean {
api.ResetAPI(db)
} else {
if len(load) > 1 {
// ignore the error, is already logged
api.SeedAPI(db, load, api.FileList())
if generate {
api.GenerateAPI(viper.GetString("export.location"), db)
} else {
if backup {
api.BackupAPI(viper.GetInt("backup.rotation"), viper.GetString("backup.location"), db)
if len(load) > 2 {
// ignore the error, is already logged
api.SeedAPI(db, load, api.FileList())
} else {
cmd.Help()
if backup {
api.BackupAPI(viper.GetInt("backup.rotation"), viper.GetString("backup.location"), db)
} else {
cmd.Help()
}
}
}
}
Expand All @@ -45,6 +50,7 @@ Command first tests connection to DB. If succeeds it will initiate, load or back
func init() {
rootCmd.AddCommand(dbCmd)

dbCmd.PersistentFlags().BoolVarP(&generate, "generate", "g", false, `Generate demo data and save them into ./data folder`)
dbCmd.PersistentFlags().StringVarP(&load, "load", "l", "", `Truncate DB table/tables and load initial data from files in folder ./data. Options:
all - load all tables
rates | consultants | projects | holidays | reported_records - load selected table`)
Expand Down
51 changes: 34 additions & 17 deletions server/documentation/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Below is default and commented `timesheet.yaml` configuration file shipped with
```
### Default configuration file
######################
# Reporting settings #
dailyWorkingHours: 8 # Used to calculate weekly and monthly expected working hours, can be changed in UI
dailyWorkingHoursMin: 8 # Used to highlight if reported less, can be changed in UI
dailyWorkingHoursMax: 12 # Used to highlight if reported more, can be changed in UI
Expand All @@ -46,14 +49,15 @@ yearlySickDays: 2 # Used to calculate weekly and monthly expected working hours,
# Categorize all rates into one of these types used on Reported Overview page
isWorking: "work" # when consultant works, can be changed in UI
isNonWorking: "not-work" # when consultant does not work, examples: vacation, sick, personal day, public holiday, vacation, unpaid leave, ..., can be changed in UI
isNonWorking: "not-work" # when consultant dows not work, examples: vacation, sick, personal day, public holiday, vacation, unpaid leave, ..., can be changed in UI
########################
# Application settings #
SSL: false # true/false, if server has SSL certificate set to true to use HTTPS, false = HTTP
SSL: false # true/false, if server has SSL certificate set to true to use HTTPS, false = HTTP
GIN_MODE: "release" # "debug" or "release" - switch server app mode
url: "" # URL on which application is running
PORT: "3000" # port on which application is running
url: "timesheet.simplesw.net" # URL on which application is running
# PORT: "443" # port on which application is running
PORT: "8080" # port on which application is running
# DB type
dbType: "postgresql" # allowed types postgresql or mysql
Expand All @@ -71,11 +75,11 @@ uploadFolderTemp: "data/uploaded/temp"
# csv data files which are loaded via command "timesheet db --load all"
data:
consultants: "consultants_prod.csv"
rates: "rates_prod.csv"
projects: "projects_prod.csv"
reportedRecords: "reported_records_prod.csv"
holidays: "holidays_cz_2019.csv"
consultants: "consultants_cloud.csv"
rates: "rates_cloud.csv"
projects: "projects_cloud.csv"
reportedRecords: "reported_records_cloud.csv"
holidays: "holidays_us_2019.csv"
export:
location: "data/exported" # select an empty and an existing folder
Expand All @@ -86,13 +90,14 @@ export:
# DB backup settings - backup data can be imported directly by a command "timesheet db --load all"
backup:
location: "data/backups" # select an empty and an existing folder relative to timesheet/data folder
rotation: 14 # how many backups back will be kept
interval: "daily" # daily or weekly - how often the DB backup should be done
rotation: 14 # how many backups back will be kept
interval: "daily" # daily or weekly - how often the DB backup should be done
# DB credentials
# used for development and testing. Ignored if DATABASE_URL is set
postgresql:
host: "127.0.0.1"
# host: "db" #
host: "127.0.0.1" #
port: "5432"
name: "timesheet"
user: "timesheet"
Expand All @@ -110,14 +115,14 @@ mysql:
`.\timesheet.exe` or `./timesheet.bin` or `./timesheet.app`
```
Web based timesheet application with DB persistence.
Application reads DB and server configuration from timesheet.toml, loads default data if DB is empty and launch web GUI.
Application reads DB and server configuration from timesheet.toml, loads default data if DB is empty and launch web GUI.
Usage:
timesheet [command]
Available Commands:
db Initiate, load or backup DB. See timesheet help db
db Initiate, load. backup DB og generate demo data. See timesheet help db
help Help about any command
routes Prints the list of all available routes
server Starts the server on URL and port defined in config.yaml
Expand All @@ -132,16 +137,17 @@ Use "timesheet [command] --help" for more information about a command.

`.\timesheet.exe help db` or `./timesheet.bin help db` or `./timesheet.app help db`
```
Initiate, load, or backup DB.
Initiate, load, backup DB or generate demo data.
Command first tests connection to DB. If succeeds it will initiate, load or backup db and exit.
Command first tests connection to DB. If succeeds it will initiate, load, backup db or generate demo data and exit.
Usage:
timesheet db [flags]
Flags:
-b, --backup Backup all DB tables in the format used by db --load command
-c, --clean Drop and create all required DB tables
-g, --generate Generate demo data and save them into ./data folder
-h, --help help for db
-l, --load string Truncate DB table/tables and load initial data from files in folder ./data. Options:
all - load all tables
Expand Down Expand Up @@ -244,6 +250,17 @@ If you or your organization would like to help beta test a Pro version of Timesh

# Release Notes

## Version 1.4.4
Released on September 24, 2019

### New Features
* Administration - ability to manage consultants and projects from UI
* Show managed data statistics on Administration and home page
* Server can generate demo data via command line

### Technical
* Use Go 1.13 and Quasar 1.1

## Version 1.4.3
Released on July 23, 2019

Expand Down
Binary file modified server/documentation/statics/administration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 29 additions & 4 deletions server/models/consultants.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ type ConsultantManager struct {
db *DB
}

var consultantDemoData = []ConsultantCSV{
{CreatedAt: DateTime{time.Now()}, Name: "Stanislav Valasek", Allocation: 1, Disabled: false},
{CreatedAt: DateTime{time.Now()}, Name: "Evan You", Allocation: 1, Disabled: false},
{CreatedAt: DateTime{time.Now()}, Name: "Razvan Stoenescu", Allocation: 1, Disabled: false},
{CreatedAt: DateTime{time.Now()}, Name: "Russ Cox", Allocation: 1, Disabled: false},
{CreatedAt: DateTime{time.Now()}, Name: "David Heinemeier Hansson", Allocation: 1, Disabled: false},
{CreatedAt: DateTime{time.Now()}, Name: "Guido van Rossum", Allocation: 1, Disabled: false},
{CreatedAt: DateTime{time.Now()}, Name: "Dan Abramov", Allocation: 1, Disabled: false},
}

// NewConsultantManager - Create a consultant manager that can be used for retrieving consultants
func NewConsultantManager(db *DB) (*ConsultantManager, error) {

Expand Down Expand Up @@ -158,16 +168,31 @@ func (db *ConsultantManager) ConsultantBackup(filePath string) (int, error) {

consultants := []*Consultant{}
db.db.Find(&consultants).Where("DeletedAt = ?", nil)
projectCSV := []*ConsultantCSV{}
consultantCSV := []*ConsultantCSV{}
for _, r := range consultants {
createdAt := DateTime{r.CreatedAt}
item := ConsultantCSV{CreatedAt: createdAt, Name: r.Name, Allocation: r.Allocation, Disabled: r.Disabled}
projectCSV = append(projectCSV, &item)
consultantCSV = append(consultantCSV, &item)
}

err = gocsv.MarshalFile(&projectCSV, consultantsFile)
err = gocsv.MarshalFile(&consultantCSV, consultantsFile)
if err != nil {
return 0, err
}
return len(consultantCSV), nil
}

// ConsultantGenerate generates test data
func (db *ConsultantManager) ConsultantGenerate(filePath string) (int, error) {
consultantsFile, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
return 0, err
}
defer consultantsFile.Close()

err = gocsv.MarshalFile(&consultantDemoData, consultantsFile)
if err != nil {
return 0, err
}
return len(consultants), nil
return len(consultantDemoData), nil
}
10 changes: 5 additions & 5 deletions server/models/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ type Date struct {

// EntityOverview -
type EntityOverview struct {
Name string `json:"name"`
Total int `json:"total"`
Active int `json:"active"`
Disabled int `json:"disabled"`
Deleted int `json:"deleted"`
Name string `json:"name"`
Total int `json:"total"`
Active int `json:"active"`
Disabled int `json:"disabled"`
Deleted int `json:"deleted"`
}

// MarshalCSV Convert the internal date as CSV string
Expand Down
40 changes: 40 additions & 0 deletions server/models/holidays.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,43 @@ func (db *HolidayManager) HolidayBackup(filePath string) (int, error) {
}
return len(holidays), nil
}

// HolidayGenerate generates test data
func (db *HolidayManager) HolidayGenerate(filePath string) (int, error) {
holidaysFile, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
return 0, err
}
defer holidaysFile.Close()
type h struct {
Date string
Description string
}
hh := []h{
{Date: "2019-01-01", Description: "New Year’s Day"},
{Date: "2019-01-21", Description: "Birthday of Martin Luther King, Jr."},
{Date: "2019-02-18", Description: "Washington’s Birthday"},
{Date: "2019-05-27", Description: "Memorial Day"},
{Date: "2019-07-04", Description: "Independence Day"},
{Date: "2019-09-02", Description: "Labor Day"},
{Date: "2019-10-14", Description: "Columbus Day"},
{Date: "2019-11-11", Description: "Veterans Day"},
{Date: "2019-11-28", Description: "Thanksgiving Day"},
{Date: "2019-12-25", Description: "Christmas Day"},
}
var holidaysCSV []HolidayCSV
d := DateTime{time.Now()}
for _, v := range hh {
dd, err := time.Parse("2006-01-02", v.Date)
if err != nil {
return 0, err
}
holidaysCSV = append(holidaysCSV, HolidayCSV{CreatedAt: d, Date: Date{dd}, Description: v.Description})
}

err = gocsv.MarshalFile(&holidaysCSV, holidaysFile)
if err != nil {
return 0, err
}
return len(holidaysCSV), nil
}
Loading

0 comments on commit 2709f1e

Please sign in to comment.