Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: add python 3.13 to test matrix #1150

Draft
wants to merge 18 commits into
base: 2.3.0
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -107,6 +107,12 @@ jobs:
matrix:
python-version: ['3.8', '3.12']
db-backend: [mysql, postgres]
# test Python 3.13 already
# release is scheduled for 2024-10-01 (https://peps.python.org/pep-0719/)
# when released: replace "python-version: ['3.8', '3.12']" with python-version: ['3.8', '3.13']
include:
- python-version: '3.13'
db-backend: postgres
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -36,8 +36,12 @@ dist

rdmo/management/static

rdmo/projects/static/projects/js/projects.js
rdmo/core/static/core/js/base.js
rdmo/core/static/core/fonts
rdmo/core/static/core/css/base.css

rdmo/projects/static/projects/js/*.js
rdmo/projects/static/projects/fonts
rdmo/projects/static/projects/css/projects.css
rdmo/projects/static/projects/css/*.css

screenshots
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file added rdmo/core/assets/img/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
260 changes: 260 additions & 0 deletions rdmo/core/assets/img/rdmo-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions rdmo/core/assets/js/actions/actionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const UPDATE_CONFIG = 'UPDATE_CONFIG'
export const DELETE_CONFIG = 'DELETE_CONFIG'

export const ADD_TO_PENDING = 'ADD_TO_PENDING'
export const REMOVE_FROM_PENDING = 'REMOVE_FROM_PENDING'

export const FETCH_SETTINGS_ERROR = 'FETCH_SETTINGS_ERROR'
export const FETCH_SETTINGS_INIT = 'FETCH_SETTINGS_INIT'
export const FETCH_SETTINGS_SUCCESS = 'FETCH_SETTINGS_SUCCESS'

export const FETCH_TEMPLATES_ERROR = 'FETCH_TEMPLATES_ERROR'
export const FETCH_TEMPLATES_INIT = 'FETCH_TEMPLATES_INIT'
export const FETCH_TEMPLATES_SUCCESS = 'FETCH_TEMPLATES_SUCCESS'

export const FETCH_CURRENT_USER_ERROR = 'FETCH_CURRENT_USER_ERROR'
export const FETCH_CURRENT_USER_INIT = 'FETCH_CURRENT_USER_INIT'
export const FETCH_CURRENT_USER_SUCCESS = 'FETCH_CURRENT_USER_SUCCESS'
9 changes: 9 additions & 0 deletions rdmo/core/assets/js/actions/configActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { UPDATE_CONFIG, DELETE_CONFIG } from './actionTypes'

export function updateConfig(path, value, ls = false) {
return {type: UPDATE_CONFIG, path, value, ls}
}

export function deleteConfig(path, ls = false) {
return {type: DELETE_CONFIG, path, ls}
}
9 changes: 9 additions & 0 deletions rdmo/core/assets/js/actions/pendingActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ADD_TO_PENDING, REMOVE_FROM_PENDING } from './actionTypes'

export function addToPending(item) {
return {type: ADD_TO_PENDING, item}
}

export function removeFromPending(item) {
return {type: REMOVE_FROM_PENDING, item}
}
25 changes: 25 additions & 0 deletions rdmo/core/assets/js/actions/settingsActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import CoreApi from '../api/CoreApi'

import { FETCH_SETTINGS_ERROR, FETCH_SETTINGS_INIT, FETCH_SETTINGS_SUCCESS } from './actionTypes'

export function fetchSettings() {
return function(dispatch) {
dispatch(fetchSettingsInit())

return CoreApi.fetchSettings()
.then((settings) => dispatch(fetchSettingsSuccess(settings)))
.catch((errors) => dispatch(fetchSettingsError(errors)))
}
}

export function fetchSettingsInit() {
return {type: FETCH_SETTINGS_INIT}
}

export function fetchSettingsSuccess(settings) {
return {type: FETCH_SETTINGS_SUCCESS, settings}
}

export function fetchSettingsError(errors) {
return {type: FETCH_SETTINGS_ERROR, errors}
}
25 changes: 25 additions & 0 deletions rdmo/core/assets/js/actions/templateActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import CoreApi from '../api/CoreApi'

import { FETCH_TEMPLATES_ERROR, FETCH_TEMPLATES_INIT, FETCH_TEMPLATES_SUCCESS } from './actionTypes'

export function fetchTemplates() {
return function(dispatch) {
dispatch(fetchTemplatesInit())

return CoreApi.fetchTemplates()
.then((templates) => dispatch(fetchTemplatesSuccess(templates)))
.catch((errors) => dispatch(fetchTemplatesError(errors)))
}
}

export function fetchTemplatesInit() {
return {type: FETCH_TEMPLATES_INIT}
}

export function fetchTemplatesSuccess(templates) {
return {type: FETCH_TEMPLATES_SUCCESS, templates}
}

export function fetchTemplatesError(errors) {
return {type: FETCH_TEMPLATES_ERROR, errors}
}
25 changes: 25 additions & 0 deletions rdmo/core/assets/js/actions/userActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import AccountsApi from '../api/AccountsApi'

import { FETCH_CURRENT_USER_ERROR, FETCH_CURRENT_USER_INIT, FETCH_CURRENT_USER_SUCCESS } from './actionTypes'

export function fetchCurrentUser() {
return function(dispatch) {
dispatch(fetchCurrentUserInit())

return AccountsApi.fetchCurrentUser(true)
.then(currentUser => dispatch(fetchCurrentUserSuccess({ currentUser })))
.catch(error => dispatch(fetchCurrentUserError(error)))
}
}

export function fetchCurrentUserInit() {
return {type: FETCH_CURRENT_USER_INIT}
}

export function fetchCurrentUserSuccess(currentUser) {
return {type: FETCH_CURRENT_USER_SUCCESS, currentUser}
}

export function fetchCurrentUserError(error) {
return {type: FETCH_CURRENT_USER_ERROR, error}
}
File renamed without changes.
24 changes: 23 additions & 1 deletion rdmo/core/assets/js/api/BaseApi.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Cookies from 'js-cookie'
import isUndefined from 'lodash/isUndefined'

import baseUrl from '../utils/baseUrl'
import { baseUrl } from '../utils/meta'

function ApiError(statusText, status) {
this.status = status
@@ -52,6 +52,28 @@ class BaseApi {
})
}

static postFormData(url, formData) {
return fetch(baseUrl + url, {
method: 'POST',
headers: {
'X-CSRFToken': Cookies.get('csrftoken')
},
body: formData
}).catch(error => {
throw new ApiError(error.message)
}).then(response => {
if (response.ok) {
return response.json()
} else if (response.status == 400) {
return response.json().then(errors => {
throw new ValidationError(errors)
})
} else {
throw new ApiError(response.statusText, response.status)
}
})
}

static put(url, data) {
return fetch(baseUrl + url, {
method: 'PUT',
4 changes: 4 additions & 0 deletions rdmo/core/assets/js/api/CoreApi.js
Original file line number Diff line number Diff line change
@@ -14,6 +14,10 @@ class CoreApi extends BaseApi {
return this.get('/api/v1/core/groups/')
}

static fetchTemplates() {
return this.get('/api/v1/core/templates/')
}

}

export default CoreApi
1 change: 1 addition & 0 deletions rdmo/core/assets/js/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'bootstrap-sass'
16 changes: 16 additions & 0 deletions rdmo/core/assets/js/components/Html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
import { isEmpty } from 'lodash'

const Html = ({ html = '' }) => {
return !isEmpty(html) && (
<div dangerouslySetInnerHTML={{ __html: html }} />
)
}

Html.propTypes = {
className: PropTypes.string,
html: PropTypes.string
}

export default Html
29 changes: 15 additions & 14 deletions rdmo/core/assets/js/components/Modal.js
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@ import React from 'react'
import PropTypes from 'prop-types'
import { Modal as BootstrapModal } from 'react-bootstrap'

const Modal = ({ bsSize, buttonLabel, buttonProps, title, show, onClose, onSave, children }) => {
const Modal = ({ title, show, modalProps, submitLabel, submitProps, onClose, onSubmit, children }) => {
return (
<BootstrapModal bsSize={bsSize} className="element-modal" onHide={onClose} show={show}>
<BootstrapModal className="element-modal" onHide={onClose} show={show} {...modalProps}>
<BootstrapModal.Header closeButton>
<h2 className="modal-title">{title}</h2>
</BootstrapModal.Header>
@@ -15,26 +15,27 @@ const Modal = ({ bsSize, buttonLabel, buttonProps, title, show, onClose, onSave,
<button type="button" className="btn btn-default" onClick={onClose}>
{gettext('Close')}
</button>
{ onSave ?
<button type="button" className="btn btn-primary" onClick={onSave} {...buttonProps}>
{buttonLabel ?? gettext('Save')}
</button>
: null
{
onSubmit && (
<button type="button" className="btn btn-primary" onClick={onSubmit} {...submitProps}>
{submitLabel ?? gettext('Save')}
</button>
)
}
</BootstrapModal.Footer>
</BootstrapModal>
)
}

Modal.propTypes = {
bsSize: PropTypes.oneOf(['lg', 'large', 'sm', 'small']),
buttonLabel: PropTypes.string,
buttonProps: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
onClose: PropTypes.func.isRequired,
onSave: PropTypes.func,
show: PropTypes.bool.isRequired,
title: PropTypes.string.isRequired,
show: PropTypes.bool.isRequired,
modalProps: PropTypes.object,
submitLabel: PropTypes.string,
submitProps: PropTypes.object,
onClose: PropTypes.func.isRequired,
onSubmit: PropTypes.func,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
}

export default Modal
24 changes: 24 additions & 0 deletions rdmo/core/assets/js/containers/Pending.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { isEmpty } from 'lodash'

const Pending = ({ pending }) => {
return (
!isEmpty(pending.items) && (
<i className="fa fa-circle-o-notch fa-spin fa-fw"></i>
)
)
}

Pending.propTypes = {
pending: PropTypes.object.isRequired,
}

function mapStateToProps(state) {
return {
pending: state.pending,
}
}

export default connect(mapStateToProps)(Pending)
22 changes: 22 additions & 0 deletions rdmo/core/assets/js/reducers/configReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { updateConfig, deleteConfig, setConfigInLocalStorage, deleteConfigInLocalStorage } from '../utils/config'

import { DELETE_CONFIG, UPDATE_CONFIG } from '../actions/actionTypes'

const initialState = {}

export default function configReducer(state = initialState, action) {
switch(action.type) {
case UPDATE_CONFIG:
if (action.ls) {
setConfigInLocalStorage(state.prefix, action.path, action.value)
}
return updateConfig(state, action.path, action.value)
case DELETE_CONFIG:
if (action.ls) {
deleteConfigInLocalStorage(state.prefix, action.path)
}
return deleteConfig(state, action.path)
default:
return state
}
}
16 changes: 16 additions & 0 deletions rdmo/core/assets/js/reducers/pendingReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ADD_TO_PENDING, REMOVE_FROM_PENDING } from '../actions/actionTypes'

const initialState = {
items: []
}

export default function pendingReducer(state = initialState, action) {
switch(action.type) {
case ADD_TO_PENDING:
return { ...state, items: [...state.items, action.item] }
case REMOVE_FROM_PENDING:
return { ...state, items: state.items.filter((item) => (item != action.item)) }
default:
return state
}
}
14 changes: 14 additions & 0 deletions rdmo/core/assets/js/reducers/settingsReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FETCH_SETTINGS_ERROR, FETCH_SETTINGS_SUCCESS } from '../actions/actionTypes'

const initialState = {}

export default function settingsReducer(state = initialState, action) {
switch(action.type) {
case FETCH_SETTINGS_SUCCESS:
return { ...state, ...action.settings }
case FETCH_SETTINGS_ERROR:
return { ...state, errors: action.errors }
default:
return state
}
}
14 changes: 14 additions & 0 deletions rdmo/core/assets/js/reducers/templateReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { FETCH_TEMPLATES_ERROR, FETCH_TEMPLATES_SUCCESS } from '../actions/actionTypes'

const initialState = {}

export default function templateReducer(state = initialState, action) {
switch(action.type) {
case FETCH_TEMPLATES_SUCCESS:
return { ...state, ...action.templates }
case FETCH_TEMPLATES_ERROR:
return { ...state, errors: action.errors }
default:
return state
}
}
18 changes: 18 additions & 0 deletions rdmo/core/assets/js/reducers/userReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { FETCH_CURRENT_USER_ERROR, FETCH_CURRENT_USER_INIT, FETCH_CURRENT_USER_SUCCESS } from '../actions/actionTypes'

const initialState = {
currentUser: {},
}

export default function userReducer(state = initialState, action) {
switch(action.type) {
case FETCH_CURRENT_USER_INIT:
return {...state, ...action.currentUser}
case FETCH_CURRENT_USER_SUCCESS:
return {...state, ...action.currentUser}
case FETCH_CURRENT_USER_ERROR:
return {...state, errors: action.error.errors}
default:
return state
}
}
2 changes: 0 additions & 2 deletions rdmo/core/assets/js/utils/baseUrl.js

This file was deleted.

58 changes: 58 additions & 0 deletions rdmo/core/assets/js/utils/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { set, unset, toNumber, isNaN } from 'lodash'

const updateConfig = (config, path, value) => {
const newConfig = {...config}
set(newConfig, path, value)
return newConfig
}

const deleteConfig = (config, path) => {
const newConfig = {...config}
unset(newConfig, path)
return newConfig
}

const getConfigFromLocalStorage = (prefix) => {
const ls = {...localStorage}

return Object.entries(ls)
.filter(([lsPath,]) => lsPath.startsWith(prefix))
.map(([lsPath, lsValue]) => {
if (lsPath.startsWith(prefix)) {
const path = lsPath.replace(`${prefix}.`, '')

// check if it is literal 'true' or 'false'
if (lsValue === 'true') {
return [path, true]
} else if (lsValue === 'false') {
return [path, false]
}

// check if the value is number or a string
const numberValue = toNumber(lsValue)
if (isNaN(numberValue)) {
return [path, lsValue]
} else {
return [path, numberValue]
}
} else {
return null
}
})
}

const setConfigInLocalStorage = (prefix, path, value) => {
localStorage.setItem(`${prefix}.${path}`, value)
}

const deleteConfigInLocalStorage = (prefix, path) => {
localStorage.removeItem(`${prefix}.${path}`)
}

export {
updateConfig,
deleteConfig,
getConfigFromLocalStorage,
setConfigInLocalStorage,
deleteConfigInLocalStorage
}
5 changes: 1 addition & 4 deletions rdmo/core/assets/js/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
export * from './api'
export { default as baseUrl } from './baseUrl'
export { default as language } from './language'
export { default as siteId } from './siteId'
export { default as staticUrl } from './staticUrl'
export { baseUrl, language, siteId, staticUrl } from './meta'
2 changes: 0 additions & 2 deletions rdmo/core/assets/js/utils/language.js

This file was deleted.

9 changes: 9 additions & 0 deletions rdmo/core/assets/js/utils/meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// take information from the <head> of the django template

export const baseUrl = document.querySelector('meta[name="baseurl"]').content.replace(/\/+$/, '')

export const staticUrl = document.querySelector('meta[name="staticurl"]').content.replace(/\/+$/, '')

export const siteId = Number(document.querySelector('meta[name="site_id"]').content)

export const language = document.querySelector('meta[name="language"]').content
2 changes: 0 additions & 2 deletions rdmo/core/assets/js/utils/siteId.js

This file was deleted.

2 changes: 0 additions & 2 deletions rdmo/core/assets/js/utils/staticUrl.js

This file was deleted.

14 changes: 14 additions & 0 deletions rdmo/core/assets/js/utils/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Cookies from 'js-cookie'
import isEmpty from 'lodash/isEmpty'

const checkStoreId = () => {
const currentStoreId = Cookies.get('storeid')
const localStoreId = localStorage.getItem('rdmo.storeid')

if (isEmpty(localStoreId) || localStoreId !== currentStoreId) {
localStorage.clear()
localStorage.setItem('rdmo.storeid', currentStoreId)
}
}

export { checkStoreId }
15 changes: 15 additions & 0 deletions rdmo/core/assets/scss/base.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
$icon-font-path: "bootstrap-sass/assets/fonts/bootstrap/";
@import '~bootstrap-sass';
@import '~font-awesome/css/font-awesome.css';

@import 'react-datepicker/dist/react-datepicker.css';

@import 'variables';
@import 'style';

@import 'codemirror';
@import 'fonts';
@import 'footer';
@import 'header';
@import 'swagger';
@import 'utils';
9 changes: 9 additions & 0 deletions rdmo/core/assets/scss/codemirror.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.CodeMirror {
font-family: DroidSans-Mono, mono;
}

formgroup .CodeMirror {
border-radius: 4px;
border: 1px solid #ccc;
color: #555;
}
44 changes: 44 additions & 0 deletions rdmo/core/assets/scss/fonts.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@font-face {
font-family: "DroidSans";
src: url('../fonts/DroidSans.ttf');
}
@font-face {
font-family: "DroidSans";
src: url('../fonts/DroidSans-Bold.ttf');
font-weight: bold;
}
@font-face {
font-family: "DroidSans-Mono";
src: url('../fonts/DroidSansMono.ttf');
}
@font-face {
font-family: "DroidSerif";
src: url('../fonts/DroidSerif.ttf');
}
@font-face {
font-family: "DroidSerif";
src: url('../fonts/DroidSerif-Bold.ttf');
font-weight: bold;
}
@font-face {
font-family: "DroidSerif";
src: url('../fonts/DroidSerif-Italic.ttf');
font-style: italic;
}
@font-face {
font-family: "DroidSerif";
src: url('../fonts/DroidSerif-BoldItalic.ttf');
font-style: italic;
font-weight: bold;
}

body {
font-family: DroidSans, sans;
}
h1, h2, h3, h4, h5, h6 {
font-family: DroidSerif, serif;
}

a.fa {
text-decoration: none !important;
}
55 changes: 55 additions & 0 deletions rdmo/core/assets/scss/footer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
$footer-height: 280px;
$footer-height-md: 600px;
$footer-height-sm: 260px;

/* footer layout */

.content {
min-height: 100%;
margin-bottom: -$footer-height;
padding-bottom: $footer-height;
}
footer {
height: $footer-height;
}
@media (max-width: $screen-sm-max) {
.content {
margin-bottom: -$footer-height-md;
padding-bottom: $footer-height-md;
}
footer {
height: $footer-height-md;
}
}
@media (max-width: $screen-xs-max) {
.content {
margin-bottom: -$footer-height-md;
padding-bottom: $footer-height-md;
}
footer {
height: $footer-height-md;
}
}

/* footer style */

footer {
color: $footer-color;
background-color: $footer-background-color;
padding-top: 20px;

a,
a:visited,
a:hover {
color: $footer-link-color;
}
h4 {
color: $footer-link-color;
}
p {
text-align: left;
}
img {
display: block;
}
}
89 changes: 89 additions & 0 deletions rdmo/core/assets/scss/header.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
$header-height: 400px;
$header-height-md: 300px;

header {
position: relative;

height: $header-height;
background-color: black;

.header-image {
position: absolute;
left: 0;
right: 0;

opacity: 0;
-webkit-transition: $image-transition;
-moz-transition: $image-transition;
-ms-transition: $image-transition;
-o-transition: $image-transition;
transition: $image-transition;

&.visible {
opacity: 1;
}
img {
display: block;
width: 100%;
height: $header-height;
}
p {
position: absolute;
bottom: 0;
right: 0;
z-index: 10;

padding-right: 5px;
margin-bottom: 5px;
font-size: 10px;
color: $footer-link-color;

}
a,
a:visited,
a:hover {
color: $footer-link-color;
}
}
.header-text {
position: relative;
padding-top: 100px;

h1 {
font-size: 60px;
color: white;
}
p {
font-size: 30px;
color: white;
}
}
}
@media (max-width: $screen-md-max) {
header {
height: $header-height-md;
}
header .header-image img {
height: $header-height-md;
}
header .header-text {
padding-top: 50px;
}
}
@media (max-width: $screen-xs-max) {
header {
background-color: inherit;
height: auto;
}
header .header-text {
padding-top: 0;
}
header .header-text h1 {
font-size: 40px;
color: $headline-color;
}
header .header-text p {
font-size: 20px;
color: $variant-color;
}
}
497 changes: 497 additions & 0 deletions rdmo/core/assets/scss/style.scss

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions rdmo/core/assets/scss/swagger.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.topbar {
background-color: $headline-color !important;
}

.swagger-ui .info {
margin: 30px;
}

.swagger-ui .btn.authorize {
border-color: $footer-background-color;
color: $text-color;
}

.swagger-ui .btn.authorize svg {
fill: $footer-background-color;
}

.swagger-ui .btn.authorize {
color: $footer-background-color !important;
}

.topbar img {
filter: hue-rotate(180deg)
}

.download-url-wrapper .download-url-button {
background-color: $headline-color !important;
}
92 changes: 92 additions & 0 deletions rdmo/core/assets/scss/utils.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
.flip {
transform: rotate(180deg) scaleX(-1);
}

.w-100 {
width: 100%;
}
.mt-0 {
margin-top: 0;
}
.mt-5 {
margin-top: 5px;
}
.mt-10 {
margin-top: 10px;
}
.mt-20 {
margin-top: 20px;
}
.mr-0 {
margin-right: 0;
}
.mr-5 {
margin-right: 5px;
}
.mr-10 {
margin-right: 10px;
}
.mr-20 {
margin-right: 20px;
}
.mb-0 {
margin-bottom: 0;
}
.mb-5 {
margin-bottom: 5px;
}
.mb-10 {
margin-bottom: 10px;
}
.mb-20 {
margin-bottom: 20px;
}
.ml-0 {
margin-left: 0;
}
.ml-5 {
margin-left: 5px;
}
.ml-10 {
margin-left: 10px;
}
.ml-20 {
margin-left: 20px;
}

.pt-0 {
padding-top: 0;
}
.pt-10 {
padding-top: 10px;
}
.pt-20 {
padding-top: 20px;
}
.pr-0 {
padding-right: 0;
}
.pr-10 {
padding-right: 10px;
}
.pr-20 {
padding-right: 20px;
}
.pb-0 {
padding-bottom: 0;
}
.pb-10 {
padding-bottom: 10px;
}
.pb-20 {
padding-bottom: 20px;
}
.pl-0 {
padding-left: 0;
}
.pl-10 {
padding-left: 10px;
}
.pl-20 {
padding-left: 20px;
}
2 changes: 2 additions & 0 deletions rdmo/core/settings.py
Original file line number Diff line number Diff line change
@@ -218,6 +218,8 @@
'PROJECT_TABLE_PAGE_SIZE'
]

TEMPLATES_API = []

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = 'info@example.com'

32 changes: 32 additions & 0 deletions rdmo/core/tests/test_viewset_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pytest

from django.urls import reverse

users = (
('owner', 'owner'),
('manager', 'manager'),
('author', 'author'),
('guest', 'guest'),
('api', 'api'),
('user', 'user'),
('anonymous', None),
)

status_map = {
'list': {
'owner': 200, 'manager': 200, 'author': 200, 'guest': 200, 'api': 200, 'user': 200, 'anonymous': 401
}
}

urlnames = {
'list': 'template-list',
}


@pytest.mark.parametrize('username,password', users)
def test_list(db, client, username, password):
client.login(username=username, password=password)

url = reverse(urlnames['list'])
response = client.get(url)
assert response.status_code == status_map['list'][username], response.json()
3 changes: 2 additions & 1 deletion rdmo/core/urls/v1.py
Original file line number Diff line number Diff line change
@@ -2,12 +2,13 @@

from rest_framework import routers

from ..viewsets import GroupViewSet, SettingsViewSet, SitesViewSet
from ..viewsets import GroupViewSet, SettingsViewSet, SitesViewSet, TemplatesViewSet

router = routers.DefaultRouter()
router.register(r'settings', SettingsViewSet, basename='setting')
router.register(r'sites', SitesViewSet, basename='site')
router.register(r'groups', GroupViewSet, basename='group')
router.register(r'templates', TemplatesViewSet, basename='template')

urlpatterns = [
path('accounts/', include('rdmo.accounts.urls.v1')),
14 changes: 14 additions & 0 deletions rdmo/core/viewsets.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from pathlib import Path

from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.sites.models import Site
from django.template.loader import get_template

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
@@ -31,3 +34,14 @@ class GroupViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = (HasModelPermission, )
queryset = Group.objects.all()
serializer_class = GroupSerializer


class TemplatesViewSet(viewsets.GenericViewSet):

permission_classes = (IsAuthenticated, )

def list(self, request, *args, **kwargs):
return Response({
Path(template_path).stem: get_template(template_path).render(request=request).strip()
for template_path in settings.TEMPLATES_API
})
2 changes: 1 addition & 1 deletion rdmo/management/assets/js/reducers/configReducer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import set from 'lodash/set'

import baseUrl from 'rdmo/core/assets/js/utils/baseUrl'
import { baseUrl } from 'rdmo/core/assets/js/utils/meta'

const initialState = {
baseUrl: baseUrl + '/management/',
4 changes: 2 additions & 2 deletions rdmo/projects/assets/js/projects.js
Original file line number Diff line number Diff line change
@@ -2,12 +2,12 @@ import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'

import configureStore from './store/configureStore'
import configureStore from './projects/store/configureStore'

import { DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'

import Main from './containers/Main'
import Main from './projects/containers/Main'

const store = configureStore()

9 changes: 9 additions & 0 deletions rdmo/projects/assets/js/projects/api/AccountsApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import BaseApi from 'rdmo/core/assets/js/api/BaseApi'

class AccountsApi extends BaseApi {
static fetchCurrentUser() {
return this.get('/api/v1/accounts/users/current/')
}
}

export default AccountsApi
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Cookies from 'js-cookie'
import BaseApi from 'rdmo/core/assets/js/api/BaseApi'
import { encodeParams } from 'rdmo/core/assets/js/utils/api'
import baseUrl from 'rdmo/core/assets/js/utils/baseUrl'
import { baseUrl } from 'rdmo/core/assets/js/utils/meta'

function BadRequestError(errors) {
this.errors = errors
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import siteId from 'rdmo/core/assets/js/utils/siteId'
import { siteId } from 'rdmo/core/assets/js/utils/meta'

const userIsManager = (currentUser) => {
if (currentUser.is_superuser ||
17 changes: 15 additions & 2 deletions webpack.config.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,18 @@ const TerserPlugin = require('terser-webpack-plugin')

// list of separate config objects for each django app and their corresponding java script applications
const configList = [
{
name: 'core',
entry: {
base: [
'./rdmo/core/assets/js/base.js',
'./rdmo/core/assets/scss/base.scss'
]
},
output: {
path: path.resolve(__dirname, './rdmo/core/static/core/'),
}
},
{
name: 'management',
entry: {
@@ -15,7 +27,6 @@ const configList = [
]
},
output: {
filename: 'js/management.js',
path: path.resolve(__dirname, './rdmo/management/static/management/'),
}
},
@@ -28,7 +39,6 @@ const configList = [
]
},
output: {
filename: 'js/projects.js',
path: path.resolve(__dirname, './rdmo/projects/static/projects/'),
}
}
@@ -42,6 +52,9 @@ const baseConfig = {
},
extensions: ['*', '.js', '.jsx']
},
output: {
filename: 'js/[name].js'
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css',

Unchanged files with check annotations Beta

client.login(username=username, password=password)
issue = Issue.objects.filter(project_id=project_id, id=issue_id).first()
url = reverse('issue_update', args=[project_id, issue_id])

Check failure on line 105 in rdmo/projects/tests/test_view_issue.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_issue_update_post[open-4-1-anonymous-None] ModuleNotFoundError: No module named 'cgi'

Check failure on line 105 in rdmo/projects/tests/test_view_issue.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_issue_update_post[in_progress-1-1-manager-manager] ModuleNotFoundError: No module named 'cgi'
data = {
'status': status
}
client.login(username=username, password=password)
issue = Issue.objects.filter(project_id=project_id, id=issue_id).first()
url = reverse('issue_send', args=[project_id, issue_id])

Check failure on line 237 in rdmo/projects/tests/test_view_issue.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_issue_send_post_integration[5-2-user-user] ModuleNotFoundError: No module named 'cgi'
data = {
'subject': 'Subject',
'message': 'Message',
def test_project_update_tasks_get(db, client, username, password, project_id):
client.login(username=username, password=password)
url = reverse('project_update_tasks', args=[project_id])

Check failure on line 446 in rdmo/projects/tests/test_view_project.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_project_update_tasks_get[1-editor-editor] ModuleNotFoundError: No module named 'cgi'
response = client.get(url)
if project_id in change_project_permission_map.get(username, []):
project_views = Project.objects.get(pk=project_id).views.all()
for view in View.objects.all():
url = reverse('project_view', args=[project_id, view.id])

Check failure on line 715 in rdmo/projects/tests/test_view_project.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_project_view[1-reviewer-reviewer] ModuleNotFoundError: No module named 'cgi'
response = client.get(url)
if project_id in view_project_permission_map.get(username, []):
project_views = Project.objects.get(pk=project_id).views.all()
for view in View.objects.all():
url = reverse('project_view_export', args=[project_id, view.pk, export_format])

Check failure on line 738 in rdmo/projects/tests/test_view_project.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_project_view_export[rtf-6-author-author] ModuleNotFoundError: No module named 'cgi'
response = client.get(url)
if project_id in view_project_permission_map.get(username, []):
client.login(username=username, password=password)
value = Value.objects.filter(project_id=project_id, id=value_id).first()
url = reverse(urlnames['detail'], args=[project_id, value_id])

Check failure on line 230 in rdmo/projects/tests/test_viewset_project_value.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_delete[238-4-site-site] ModuleNotFoundError: No module named 'cgi'
response = client.delete(url)
if value and project_id in delete_value_permission_map.get(username, []):
value_exists = Value.objects.filter(project_id=project_id, snapshot=None, id=value_id).exists()
values_count = Value.objects.count()
url = reverse(urlnames['set'], args=[project_id, value_id])

Check failure on line 252 in rdmo/projects/tests/test_viewset_project_value.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_set[85-20-2-anonymous-None] ModuleNotFoundError: No module named 'cgi'
response = client.delete(url)
if value_exists and project_id in delete_value_permission_map.get(username, []):
settings.PROJECT_SEND_INVITE = PROJECT_SEND_INVITE
client.login(username=username, password=password)
url = reverse('membership_create', args=[project_id])

Check failure on line 57 in rdmo/projects/tests/test_view_membership.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_membership_create_post[guest-2-site-site-False] ModuleNotFoundError: No module named 'cgi'
data = {
'username_or_email': 'user',
'role': membership_role
client.login(username=username, password=password)
membership = Membership.objects.filter(project_id=project_id, id=membership_id).first()
url = reverse('membership_update', args=[project_id, membership_id])

Check failure on line 231 in rdmo/projects/tests/test_view_membership.py

GitHub Actions / Test (Python: 3.13, DB: postgres)

test_membership_update_post[owner-2-4-guest-guest] ModuleNotFoundError: No module named 'cgi'
data = {
'role': membership_role
}