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

CRU classes #45

Merged
merged 9 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
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
Binary file modified EduSync.exe
Binary file not shown.
116 changes: 116 additions & 0 deletions adminHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,122 @@ func AdminHandler(router *mux.Router) {
// Request Body: JSON object with announcement details
// Response: HTTP Status Created (201)

// Serve the search class page
router.HandleFunc("/admin/search_class", func(res http.ResponseWriter, req *http.Request) {
t, err := template.ParseFiles("templates/admin/search_class.html")
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(res, nil)
}).Methods("GET")

// Serve the create class page
router.HandleFunc("/admin/create_class", func(res http.ResponseWriter, req *http.Request) {
t, err := template.ParseFiles("templates/admin/create_class.html")
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(res, nil)
}).Methods("GET")

// Search for classes by classID
router.HandleFunc("/admin/api/search_class", func(res http.ResponseWriter, req *http.Request) {
classID := req.URL.Query().Get("classID")
classes, err := searchClasses(classID)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
res.Header().Set("Content-Type", "application/json")
json.NewEncoder(res).Encode(classes)
}).Methods("GET")

// Edit class information page
router.HandleFunc("/admin/class/{classID}/edit", func(res http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
classID := vars["classID"]

// Retrieve students and class information
students, err := GetStudentsByClassID(classID)
if err != nil {
http.Error(res, fmt.Sprintf("error retrieving students: %v", err), http.StatusInternalServerError)
return
}

class, err := readClass(students, classID, req)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}

t, err := template.ParseFiles("templates/admin/edit_class.html")
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(res, class)
}).Methods("GET")

// Update class information
router.HandleFunc("/admin/class/{classID}", func(res http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
classID := vars["classID"]

// Retrieve students
students, err := GetStudentsByClassID(classID)
if err != nil {
http.Error(res, fmt.Sprintf("error retrieving students: %v", err), http.StatusInternalServerError)
return
}

switch req.Method {
case http.MethodGet:
class, err := readClass(students, classID, req)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
res.Header().Set("Content-Type", "application/json")
json.NewEncoder(res).Encode(class)
case http.MethodPut:
var updates map[string]interface{}
if err := json.NewDecoder(req.Body).Decode(&updates); err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
return
}
class := Class{ClassID: classID}
if err := updateClass(class, updates, req); err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
}
res.WriteHeader(http.StatusNoContent)
}
}).Methods("GET", "PUT")

// Create a new class
router.HandleFunc("/admin/class/", func(res http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodPost {
var class Class
if err := json.NewDecoder(req.Body).Decode(&class); err != nil {
http.Error(res, fmt.Sprintf(`{"error": "Invalid request payload: %v"}`, err), http.StatusBadRequest)
return
}
// class.ClassID = uuid.New().String()
// class.CreatedAt = time.Now()
// class.UpdatedAt = time.Now()
if err := createClass(class, req); err != nil {
http.Error(res, fmt.Sprintf(`{"error": "Failed to create class: %v"}`, err), http.StatusInternalServerError)
return
}
res.WriteHeader(http.StatusCreated)
json.NewEncoder(res).Encode(class)
} else {
http.Error(res, `{"error": "Invalid request method"}`, http.StatusMethodNotAllowed)
}
}).Methods("POST")

router.HandleFunc("/admin/api/profile", func(res http.ResponseWriter, req *http.Request) {
currentUser, err := GetCurrentUser(req)
if err != nil {
Expand Down
57 changes: 55 additions & 2 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,24 @@ func GetCurrentAdmin(req *http.Request) (Admin, error) {
return admin, nil
}

// Utility function to get list of students given a classID
func GetStudentsByClassID(classID string) ([]Student, error) {
ref := firebaseClient.NewRef("students")
var studentsMap map[string]Student
if err := ref.Get(context.TODO(), &studentsMap); err != nil {
return nil, fmt.Errorf("error reading students: %v", err)
}

var students []Student
for _, student := range studentsMap {
if student.ClassID == classID {
students = append(students, student)
}
}

return students, nil
}

// Utility functions to check roles
func isAdmin(user User) bool {
return user.Role == "Admin"
Expand Down Expand Up @@ -940,6 +958,41 @@ func readAllClasses(req *http.Request) ([]Class, error) {
return classes, nil
}

func readClasses() ([]Class, error) {
var classesMap map[string]Class
ref := firebaseClient.NewRef("classes")
if err := ref.Get(context.TODO(), &classesMap); err != nil {
return nil, fmt.Errorf("error reading classes: %v", err)
}
// Convert map to slice
classes := make([]Class, 0, len(classesMap))
for _, class := range classesMap {
classes = append(classes, class)
}
return classes, nil
}

func searchClasses(classID string) ([]Class, error) {
// Read all classes from the data source
classes, err := readClasses()
if err != nil {
return nil, err
}
// If the classID subject is empty, return all classes
if classID == "" {
return classes, nil
}
// Filter classes based on whether the classID contains the search term
var filteredClasses []Class
lowerSubject := strings.ToLower(classID)
for _, class := range classes {
if strings.Contains(strings.ToLower(class.Name), lowerSubject) {
filteredClasses = append(filteredClasses, class)
}
}
return filteredClasses, nil
}

func updateClass(class Class, updates map[string]interface{}, req *http.Request) error {
currentUser, err := GetCurrentUser(req)
if err != nil {
Expand Down Expand Up @@ -1019,8 +1072,8 @@ func readAnnouncements() ([]Announcement, error) {
}
// Convert map to slice
announcements := make([]Announcement, 0, len(announcementsMap))
for _, student := range announcementsMap {
announcements = append(announcements, student)
for _, announcement := range announcementsMap {
announcements = append(announcements, announcement)
}
return announcements, nil
}
Expand Down
18 changes: 9 additions & 9 deletions firebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ import (
"log"
"os"

//"github.com/joho/godotenv"
"github.com/joho/godotenv"

firebase "firebase.google.com/go"
"google.golang.org/api/option"
)

// Use godot package to load/read the .env file and
// return the value of the key (for local env)
// func goDotEnvVariable(key string) string {
func goDotEnvVariable(key string) string {

// // load .env file
// err := godotenv.Load(".env")
// load .env file
err := godotenv.Load(".env")

// if err != nil {
// log.Fatalf("Error loading .env file")
// }
if err != nil {
log.Fatalf("Error loading .env file")
}

// return os.Getenv(key)
// }
return os.Getenv(key)
}

// InitializeFirebase initializes the Firebase app and sets the global firebaseClient variable
func initializeFirebase() error {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/gorilla/sessions v1.3.0
github.com/joho/godotenv v1.5.1
github.com/markbates/goth v1.80.0
golang.org/x/oauth2 v0.21.0
google.golang.org/api v0.189.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8=
github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
1 change: 1 addition & 0 deletions templates/admin/create_announcement.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
<li><a href="/admin/search_parent">Parents</a></li>
<li><a href="/admin/search_instructor">Instructors</a></li>
<li><a href="/admin/search_announcement">Announcements</a></li>
<li><a href="/admin/search_class">Classes</a></li>
<li><a href="/admin/profile">Profile</a></li>
<li><a href="/logout">Logout</a></li>
</ul>
Expand Down
Loading
Loading