Skip to content

Commit

Permalink
Merge branch 'staging' into enhancement-default-sort-order
Browse files Browse the repository at this point in the history
  • Loading branch information
campb303 authored Feb 2, 2021
2 parents 75b2ff3 + c5f48d4 commit 7af8823
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 27 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ yarn-error.log*
/api/venv
__pycache__/
venv-manager.log
/api/.env
/api/.env
/api/webqueueapi.egg-info
34 changes: 30 additions & 4 deletions api/ECNQueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def __parseHeaders(self) -> list:
# Example:
# [ce] QTime-Updated-By: campb303 becomes
# QTime-Updated-By: campb303
queuePrefixPattern = re.compile("\[.*\] {1}")
queuePrefixPattern = re.compile(r"\[.*?\] {1}")
for lineNumber in range(self.__getHeaderBoundary()):
line = self.__rawItem[lineNumber]
lineHasQueuePrefix = queuePrefixPattern.match(line)
Expand Down Expand Up @@ -263,6 +263,12 @@ def __parseSections(self) -> list:
for assignment in assignementLsit:
sections.append(assignment)

# Checks for empty content within an item and returns and
if contentEnd <= contentStart:
blankInitialMessage = self.__initialMessageParsing([""])
sections.append(blankInitialMessage)
return sections

# Checks for Directory Identifiers
if self.__rawItem[contentStart] == "\n" and self.__rawItem[contentStart + 1].startswith("\t"):

Expand Down Expand Up @@ -915,6 +921,10 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict:
if line == "\n":
newLineCounter = newLineCounter + 1

if newLineCounter == 2 and "datetime" not in replyFromInfo.keys():
errorMessage = "Expected \"Date: [datetime]\" in the header info"
return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage)

elif line == "===============================================\n":
endingDelimiterCount = endingDelimiterCount + 1

Expand Down Expand Up @@ -1190,7 +1200,19 @@ def __getUserAlias(self) -> str:
Returns:
str: User's Career Account alias if present or empty string
"""
emailUser, emailDomain = self.userEmail.split("@")


try:
emailUser, emailDomain = self.userEmail.split("@")

# Returns an error parse if the self.useremail doesn't contain exactally one "@" symbol
except ValueError:
# Parses through the self.headers list to find the "From" header and its line number
for lineNum, header in enumerate(self.headers):
if header["type"] == "From":
headerString = header["type"] + ": " + header["content"]
return self.__errorParsing(headerString, lineNum + 1, "Expected valid email Address")

return emailUser if emailDomain.endswith("purdue.edu") else ""

def __getFormattedDate(self, date: str) -> str:
Expand Down Expand Up @@ -1331,7 +1353,11 @@ def getQueueCounts() -> list:
possibleItems = os.listdir(queueDirectory + "/" + queue)
validItems = [isValidItemName for file in possibleItems]
queueInfo.append( {"name": queue, "number_of_items": len(validItems)} )
return queueInfo

# Sorts list of queue info alphabetically
sortedQueueInfo = sorted(queueInfo, key = lambda queueInfoList: queueInfoList['name'])

return sortedQueueInfo


def loadQueues() -> list:
Expand All @@ -1345,4 +1371,4 @@ def loadQueues() -> list:
for queue in getValidQueues():
queues.append(Queue(queue))

return queues
return queues
7 changes: 6 additions & 1 deletion api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ Flask-RESTful
python-dateutil
Flask-JWT-Extended
# Prevent upgrade to 2.x until Flask-JWT-Extended is updated to support it
PyJWT == 1.*
PyJWT == 1.*

# API Documentation
mkdocs
mkdocs-material
mkautodoc
72 changes: 51 additions & 21 deletions src/components/ItemTable/ItemTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import { useHistory } from "react-router-dom";
import RelativeTime from "react-relative-time";
import ItemTableFilter from "../ItemTableFilter/"
import { ArrowDownward, ArrowUpward } from "@material-ui/icons";

import ItemTableCell from "../ItemTableCell";
import LastUpdatedCell from "../LastUpdatedCell/";

export default function ItemTable({ data, rowCanBeSelected }) {
const [selectedRow, setSelectedRow] = useState({ queue: null, number: null });

const theme = useTheme();
const useStyles = makeStyles({
// Fully visible for active icons
activeSortIcon: {
opacity: 1,
},
// Half visible for inactive icons
inactiveSortIcon: {
opacity: 0.2,
hoverBackgroundColor: {
"&:hover": {
// The !important is placed here to enforce CSS specificity.
// See: https://material-ui.com/styles/advanced/#css-injection-order
backgroundColor: `${theme.palette.primary[200]} !important`,
},
},
rowSelected: {
backgroundColor: theme.palette.type === 'light' ? theme.palette.primary[100] : theme.palette.primary[600],
Expand All @@ -39,6 +39,7 @@ export default function ItemTable({ data, rowCanBeSelected }) {

const history = useHistory();

// See React Table Column Settings: https://react-table.tanstack.com/docs/api/useTable#column-properties
const columns = React.useMemo(
() => [
{ Header: 'Queue', accessor: 'queue', },
Expand All @@ -51,8 +52,11 @@ export default function ItemTable({ data, rowCanBeSelected }) {
{ Header: 'Department', accessor: 'department' },
{ Header: 'Building', accessor: 'building' },
{ Header: 'Date Received', accessor: 'dateReceived', sortInverted: true, Cell: ({ value }) => <RelativeTime value={value} /> },
{ Header: 'Last Updated', accessor: 'lastUpdated', },
{ Header: 'Department', accessor: 'department' },
{ Header: 'Building', accessor: 'building' },
{ Header: 'Date Received', accessor: 'dateReceived', },
], []);

const tableInstance = useTable(
{
columns,
Expand All @@ -67,7 +71,7 @@ export default function ItemTable({ data, rowCanBeSelected }) {
onChange={(event) => setFilter(event.target.value)}
/>
);
}
},
},
initialState: {
sortBy: [
Expand All @@ -83,7 +87,11 @@ export default function ItemTable({ data, rowCanBeSelected }) {

return (
<TableContainer>
<Table {...getTableProps} aria-label="Table of Queue Items" >
<Table
{...getTableProps}
aria-label="Table of Queue Items"
size="small"
>
<TableHead>
{headerGroups.map(headerGroup => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
Expand Down Expand Up @@ -130,28 +138,50 @@ export default function ItemTable({ data, rowCanBeSelected }) {
))}
</TableHead>
<TableBody {...getTableBodyProps()}>
{rows.map((row, i) => {
{rows.map((row) => {
prepareRow(row);
let isSelected = selectedRow.queue === row.original.queue && selectedRow.number === row.original.number
return (
<TableRow
hover
onClick={() => {
history.push(`/${row.original.queue}/${row.original.number}`);
setSelectedRow({ queue: row.original.queue, number: row.original.number });
}}
// This functionality should be achieved by using the selected prop and
// overriding the selected class but this applied the secondary color at 0.08% opacity.
// Overridding the root class is a workaround.
classes={{ root: (isSelected && rowCanBeSelected) ? classes.rowSelected : classes.bandedRows }}
{...row.getRowProps()} >
classes={{
root: (isSelected && rowCanBeSelected) ? classes.rowSelected : classes.bandedRows,
hover: classes.hoverBackgroundColor
}}
{...row.getRowProps()}
>
{row.cells.map(cell => (
<TableCell
classes={{ root: classes.columnBorders }}
{...cell.getCellProps()}
>
{cell.render("Cell")}
</TableCell>
))}
cell.render(_ => {
switch (cell.column.id) {
case "dateReceived":
return (
<ItemTableCell TableCellProps={cell.getCellProps()}>
<RelativeTime value={cell.value} />
</ItemTableCell>
);
case "lastUpdated":
return (
<LastUpdatedCell
time={cell.value}
ItemTableCellProps={cell.getCellProps()}
/>
);
default:
return (
<ItemTableCell TableCellProps={cell.getCellProps()}>
{cell.value}
</ItemTableCell>
);
}
})
))};
</TableRow>
);
})}
Expand Down
37 changes: 37 additions & 0 deletions src/components/ItemTableCell/ItemTableCell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'
import PropTypes from "prop-types";
import { makeStyles, TableCell, useTheme } from '@material-ui/core'

export default function ItemTableCell({ children, TableCellProps }) {
const theme = useTheme();
const useStyles = makeStyles({
columnBorders: {
// Add column borders
borderLeftWidth: "1px",
borderLeftStyle: "solid",
borderColor: theme.palette.type === "light" ? theme.palette.grey[300] : theme.palette.grey[500]
},
})

const classes = useStyles();
return (
<TableCell
classes={{ root: classes.columnBorders }}
{...TableCellProps}
>
{children}
</TableCell>
);
}

ItemTableCell.propTypes = {
/** Child object passed to display cell data. */
"children": PropTypes.object,
/** Props applied to the TableCell component. */
"TableCellProps": PropTypes.object
};

ItemTableCell.defaultProps = {
"children": {},
"TableCellProps": {},
};
38 changes: 38 additions & 0 deletions src/components/ItemTableCell/ItemTableCell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
The ItemTableCell wraps an [MUI TableCell](https://material-ui.com/api/table-cell/) and adds styling.

## Default Usage
```jsx
import { Paper } from '@material-ui/core';
import ItemTableCell from "./ItemTableCell";

<Paper>
<ItemTableCell>
Hello, moto!
</ItemTableCell>
</Paper>
```

```jsx static
<Paper>
<ItemTableCell>
Hello, moto!
</ItemTableCell>
</Paper>
```

## Forwarded TableCell Props
Props can be passed to the TableCell component using the TableCellProps prop.
```jsx
import { Paper } from '@material-ui/core';
import ItemTableCell from "./ItemTableCell";

<ItemTableCell TableCellProps={{ component: Paper, variant: "footer" }}>
Hello, moto!
</ItemTableCell>
```

```jsx static
<ItemTableCell TableCellProps={{ component: Paper, variant: "footer" }}>
Hello, moto!
</ItemTableCell>
```
3 changes: 3 additions & 0 deletions src/components/ItemTableCell/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ItemTableCell from "./ItemTableCell";

export default ItemTableCell;
73 changes: 73 additions & 0 deletions src/components/LastUpdatedCell/LastUpdatedCell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react'
import PropTypes from "prop-types";
import { useTheme } from '@material-ui/core';
import { red } from '@material-ui/core/colors';
import RelativeTime from 'react-relative-time';
import ItemTableCell from '../ItemTableCell';

export default function LastUpdatedCell({ time, ItemTableCellProps }) {

const theme = useTheme();

/**
* Returns a color showing how old an item is.
* @param {string} time ISO 8601 formatted time string.
* @example
* // Returns "#e57373"
* timeToBackgroundColor("2021-01-04T11:47:00-0500")
* @returns {string} Hex color code.
*/
const timeToBackgroundColor = (time) => {
const lastUpdated = new Date(time).getTime();
const now = new Date().getTime();
const timeDelta = now - lastUpdated;

const day = 24 * 60 * 60 * 1000;
const week = day * 7;
const month = week * 4;

let backgroundColor = theme.palette.background.paper;

// 1-6 days old
if (timeDelta > day && timeDelta <= week) {
backgroundColor = red[100];
}
// 7-28 days old
else if (timeDelta > week && timeDelta <= month) {
backgroundColor = red[300];
}
// 29+ days old
else if (timeDelta > month) {
backgroundColor = red[500];
}

return backgroundColor;
}

// Insert the calculated background color into props so it isn't overriden.
// Inspired by: https://github.com/mui-org/material-ui/issues/19479
ItemTableCellProps = {
...ItemTableCellProps,
style: {
...ItemTableCellProps.style,
backgroundColor: timeToBackgroundColor(time)
}
};

return (
<ItemTableCell TableCellProps={ItemTableCellProps}>
<RelativeTime value={time} />
</ItemTableCell>
);
};

LastUpdatedCell.propTypes = {
/** ISO 8601 formatted time string, Date object or UNIX time. See: https://www.npmjs.com/package/react-relative-time */
"time": PropTypes.string.isRequired,
/** Props to be applied to the ItemTableCell. */
"ItemTableCellProps": PropTypes.object,
};

LastUpdatedCell.defaultProps = {
"ItemTableCellProps": {},
};
Loading

0 comments on commit 7af8823

Please sign in to comment.